alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (WindowsLinkSupport.java)

This example Java source code file (WindowsLinkSupport.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

ioexception, notlinkexception, offsetof_pathbuffer, offsetof_pathlength, offsetof_pathoffset, offsetof_reparsetag, security, string, symbolic, too, unc, windowsexception, windowsfilesystem, windowslinksupport, windowspath

The WindowsLinkSupport.java Java example source code

/*
 * Copyright (c) 2008, 2013, 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.*;
import java.io.IOException;
import java.io.IOError;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.misc.Unsafe;

import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;

/**
 * Utility methods for symbolic link support on Windows Vista and newer.
 */

class WindowsLinkSupport {
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    private WindowsLinkSupport() {
    }

    /**
     * Returns the target of a symbolic link
     */
    static String readLink(WindowsPath path) throws IOException {
        long handle = 0L;
        try {
            handle = path.openForReadAttributeAccess(false); // don't follow links
        } catch (WindowsException x) {
            x.rethrowAsIOException(path);
        }
        try {
            return readLinkImpl(handle);
        } finally {
            CloseHandle(handle);
        }
    }

    /**
     * Returns the final path (all symbolic links resolved) or null if this
     * operation is not supported.
     */
    private static String getFinalPath(WindowsPath input) throws IOException {
        long h = 0;
        try {
            h = input.openForReadAttributeAccess(true);
        } catch (WindowsException x) {
            x.rethrowAsIOException(input);
        }
        try {
            return stripPrefix(GetFinalPathNameByHandle(h));
        } catch (WindowsException x) {
            // ERROR_INVALID_LEVEL is the error returned when not supported
            // (a sym link to file on FAT32 or Samba server for example)
            if (x.lastError() != ERROR_INVALID_LEVEL)
                x.rethrowAsIOException(input);
        } finally {
            CloseHandle(h);
        }
        return null;
    }

    /**
     * Returns the final path of a given path as a String. This should be used
     * prior to calling Win32 system calls that do not follow links.
     */
    static String getFinalPath(WindowsPath input, boolean followLinks)
        throws IOException
    {
        WindowsFileSystem fs = input.getFileSystem();
        try {
            // if not following links then don't need final path
            if (!followLinks || !fs.supportsLinks())
                return input.getPathForWin32Calls();

            // if file is not a sym link then don't need final path
            if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
                return input.getPathForWin32Calls();
            }
        } catch (WindowsException x) {
            x.rethrowAsIOException(input);
        }

        // The file is a symbolic link so attempt to get the final path
        String result = getFinalPath(input);
        if (result != null)
            return result;

        // Fallback: read target of link, resolve against parent, and repeat
        // until file is not a link.
        WindowsPath target = input;
        int linkCount = 0;
        do {
            try {
                WindowsFileAttributes attrs =
                    WindowsFileAttributes.get(target, false);
                // non a link so we are done
                if (!attrs.isSymbolicLink()) {
                    return target.getPathForWin32Calls();
                }
            } catch (WindowsException x) {
                x.rethrowAsIOException(target);
            }
            WindowsPath link = WindowsPath
                .createFromNormalizedPath(fs, readLink(target));
            WindowsPath parent = target.getParent();
            if (parent == null) {
                // no parent so use parent of absolute path
                final WindowsPath t = target;
                target = AccessController
                    .doPrivileged(new PrivilegedAction<WindowsPath>() {
                        @Override
                        public WindowsPath run() {
                            return t.toAbsolutePath();
                        }});
                parent = target.getParent();
            }
            target = parent.resolve(link);

        } while (++linkCount < 32);

        throw new FileSystemException(input.getPathForExceptionMessage(), null,
            "Too many links");
    }

    /**
     * Returns the actual path of a file, optionally resolving all symbolic
     * links.
     */
    static String getRealPath(WindowsPath input, boolean resolveLinks)
        throws IOException
    {
        WindowsFileSystem fs = input.getFileSystem();
        if (resolveLinks && !fs.supportsLinks())
            resolveLinks = false;

        // Start with absolute path
        String path = null;
        try {
            path = input.toAbsolutePath().toString();
        } catch (IOError x) {
            throw (IOException)(x.getCause());
        }

        // Collapse "." and ".."
        if (path.indexOf('.') >= 0) {
            try {
                path = GetFullPathName(path);
            } catch (WindowsException x) {
                x.rethrowAsIOException(input);
            }
        }

        // string builder to build up components of path
        StringBuilder sb = new StringBuilder(path.length());

        // Copy root component
        int start;
        char c0 = path.charAt(0);
        char c1 = path.charAt(1);
        if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
            c1 == ':' && path.charAt(2) == '\\') {
            // Driver specifier
            sb.append(Character.toUpperCase(c0));
            sb.append(":\\");
            start = 3;
        } else if (c0 == '\\' && c1 == '\\') {
            // UNC pathname, begins with "\\\\host\\share"
            int last = path.length() - 1;
            int pos = path.indexOf('\\', 2);
            // skip both server and share names
            if (pos == -1 || (pos == last)) {
                // The UNC does not have a share name (collapsed by GetFullPathName)
                throw new FileSystemException(input.getPathForExceptionMessage(),
                    null, "UNC has invalid share");
            }
            pos = path.indexOf('\\', pos+1);
            if (pos < 0) {
                pos = last;
                sb.append(path).append("\\");
            } else {
                sb.append(path, 0, pos+1);
            }
            start = pos + 1;
        } else {
            throw new AssertionError("path type not recognized");
        }

        // if the result is only a root component then we simply check it exists
        if (start >= path.length()) {
            String result = sb.toString();
            try {
                GetFileAttributes(result);
            } catch (WindowsException x) {
                x.rethrowAsIOException(path);
            }
            return result;
        }

        // iterate through each component to get its actual name in the
        // directory
        int curr = start;
        while (curr < path.length()) {
            int next = path.indexOf('\\', curr);
            int end = (next == -1) ? path.length() : next;
            String search = sb.toString() + path.substring(curr, end);
            try {
                FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));
                FindClose(fileData.handle());

                // if a reparse point is encountered then we must return the
                // final path.
                if (resolveLinks &&
                    WindowsFileAttributes.isReparsePoint(fileData.attributes()))
                {
                    String result = getFinalPath(input);
                    if (result == null) {
                        // Fallback to slow path, usually because there is a sym
                        // link to a file system that doesn't support sym links.
                        WindowsPath resolved = resolveAllLinks(
                            WindowsPath.createFromNormalizedPath(fs, path));
                        result = getRealPath(resolved, false);
                    }
                    return result;
                }

                // add the name to the result
                sb.append(fileData.name());
                if (next != -1) {
                    sb.append('\\');
                }
            } catch (WindowsException e) {
                e.rethrowAsIOException(path);
            }
            curr = end + 1;
        }

        return sb.toString();
    }

    /**
     * Returns target of a symbolic link given the handle of an open file
     * (that should be a link).
     */
    private static String readLinkImpl(long handle) throws IOException {
        int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
        NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
        try {
            try {
                DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
            } catch (WindowsException x) {
                // FIXME: exception doesn't have file name
                if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
                    throw new NotLinkException(null, null, x.errorString());
                x.rethrowAsIOException((String)null);
            }

            /*
             * typedef struct _REPARSE_DATA_BUFFER {
             *     ULONG  ReparseTag;
             *     USHORT  ReparseDataLength;
             *     USHORT  Reserved;
             *     union {
             *         struct {
             *             USHORT  SubstituteNameOffset;
             *             USHORT  SubstituteNameLength;
             *             USHORT  PrintNameOffset;
             *             USHORT  PrintNameLength;
             *             WCHAR  PathBuffer[1];
             *         } SymbolicLinkReparseBuffer;
             *         struct {
             *             USHORT  SubstituteNameOffset;
             *             USHORT  SubstituteNameLength;
             *             USHORT  PrintNameOffset;
             *             USHORT  PrintNameLength;
             *             WCHAR  PathBuffer[1];
             *         } MountPointReparseBuffer;
             *         struct {
             *             UCHAR  DataBuffer[1];
             *         } GenericReparseBuffer;
             *     };
             * } REPARSE_DATA_BUFFER
             */
            final short OFFSETOF_REPARSETAG = 0;
            final short OFFSETOF_PATHOFFSET = 8;
            final short OFFSETOF_PATHLENGTH = 10;
            final short OFFSETOF_PATHBUFFER = 16 + 4;   // check this

            int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
            if (tag != IO_REPARSE_TAG_SYMLINK) {
                // FIXME: exception doesn't have file name
                throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
            }

            // get offset and length of target
            short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
            short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
            if ((nameLengthInBytes % 2) != 0)
                throw new FileSystemException(null, null, "Symbolic link corrupted");

            // copy into char array
            char[] name = new char[nameLengthInBytes/2];
            unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
                name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);

            // remove special prefix
            String target = stripPrefix(new String(name));
            if (target.length() == 0) {
                throw new IOException("Symbolic link target is invalid");
            }
            return target;
        } finally {
            buffer.release();
        }
    }

    /**
     * Resolve all symbolic-links in a given absolute and normalized path
     */
    private static WindowsPath resolveAllLinks(WindowsPath path)
        throws IOException
    {
        assert path.isAbsolute();
        WindowsFileSystem fs = path.getFileSystem();

        // iterate through each name element of the path, resolving links as
        // we go.
        int linkCount = 0;
        int elem = 0;
        while (elem < path.getNameCount()) {
            WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));

            WindowsFileAttributes attrs = null;
            try {
                attrs = WindowsFileAttributes.get(current, false);
            } catch (WindowsException x) {
                x.rethrowAsIOException(current);
            }

            /**
             * If a symbolic link then we resolve it against the parent
             * of the current name element. We then resolve any remaining
             * part of the path against the result. The target of the link
             * may have "." and ".." components so re-normalize and restart
             * the process from the first element.
             */
            if (attrs.isSymbolicLink()) {
                linkCount++;
                if (linkCount > 32)
                    throw new IOException("Too many links");
                WindowsPath target = WindowsPath
                    .createFromNormalizedPath(fs, readLink(current));
                WindowsPath remainder = null;
                int count = path.getNameCount();
                if ((elem+1) < count) {
                    remainder = path.subpath(elem+1, count);
                }
                path = current.getParent().resolve(target);
                try {
                    String full = GetFullPathName(path.toString());
                    if (!full.equals(path.toString())) {
                        path = WindowsPath.createFromNormalizedPath(fs, full);
                    }
                } catch (WindowsException x) {
                    x.rethrowAsIOException(path);
                }
                if (remainder != null) {
                    path = path.resolve(remainder);
                }

                // reset
                elem = 0;
            } else {
                // not a link
                elem++;
            }
        }

        return path;
    }

    /**
     * Strip long path or symbolic link prefix from path
     */
    private static String stripPrefix(String path) {
        // prefix for resolved/long path
        if (path.startsWith("\\\\?\\")) {
            if (path.startsWith("\\\\?\\UNC\\")) {
                path = "\\" + path.substring(7);
            } else {
                path = path.substring(4);
            }
            return path;
        }

        // prefix for target of symbolic link
        if (path.startsWith("\\??\\")) {
            if (path.startsWith("\\??\\UNC\\")) {
                path = "\\" + path.substring(7);
            } else {
                path = path.substring(4);
            }
            return path;
        }
        return path;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java WindowsLinkSupport.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.