Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/nio/fs/WindowsPath.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.nio.file.attribute.*;29import java.io.*;30import java.net.URI;31import java.util.*;32import java.lang.ref.WeakReference;3334import com.sun.nio.file.ExtendedWatchEventModifier;3536import static sun.nio.fs.WindowsNativeDispatcher.*;37import static sun.nio.fs.WindowsConstants.*;3839/**40* Windows implementation of Path41*/4243class WindowsPath extends AbstractPath {4445// The maximum path that does not require long path prefix. On Windows46// the maximum path is 260 minus 1 (NUL) but for directories it is 26047// minus 12 minus 1 (to allow for the creation of a 8.3 file in the48// directory).49private static final int MAX_PATH = 247;5051// Maximum extended-length path52private static final int MAX_LONG_PATH = 32000;5354// FIXME - eliminate this reference to reduce space55private final WindowsFileSystem fs;5657// path type58private final WindowsPathType type;59// root component (may be empty)60private final String root;61// normalized path62private final String path;6364// the path to use in Win32 calls. This differs from path for relative65// paths and has a long path prefix for all paths longer than MAX_PATH.66private volatile WeakReference<String> pathForWin32Calls;6768// offsets into name components (computed lazily)69private volatile Integer[] offsets;7071// computed hash code (computed lazily, no need to be volatile)72private int hash;737475/**76* Initializes a new instance of this class.77*/78private WindowsPath(WindowsFileSystem fs,79WindowsPathType type,80String root,81String path)82{83this.fs = fs;84this.type = type;85this.root = root;86this.path = path;87}8889/**90* Creates a Path by parsing the given path.91*/92static WindowsPath parse(WindowsFileSystem fs, String path) {93WindowsPathParser.Result result = WindowsPathParser.parse(path);94return new WindowsPath(fs, result.type(), result.root(), result.path());95}9697/**98* Creates a Path from a given path that is known to be normalized.99*/100static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,101String path,102BasicFileAttributes attrs)103{104try {105WindowsPathParser.Result result =106WindowsPathParser.parseNormalizedPath(path);107if (attrs == null) {108return new WindowsPath(fs,109result.type(),110result.root(),111result.path());112} else {113return new WindowsPathWithAttributes(fs,114result.type(),115result.root(),116result.path(),117attrs);118}119} catch (InvalidPathException x) {120throw new AssertionError(x.getMessage());121}122}123124/**125* Creates a WindowsPath from a given path that is known to be normalized.126*/127static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,128String path)129{130return createFromNormalizedPath(fs, path, null);131}132133/**134* Special implementation with attached/cached attributes (used to quicken135* file tree traveral)136*/137private static class WindowsPathWithAttributes138extends WindowsPath implements BasicFileAttributesHolder139{140final WeakReference<BasicFileAttributes> ref;141142WindowsPathWithAttributes(WindowsFileSystem fs,143WindowsPathType type,144String root,145String path,146BasicFileAttributes attrs)147{148super(fs, type, root, path);149ref = new WeakReference<BasicFileAttributes>(attrs);150}151152@Override153public BasicFileAttributes get() {154return ref.get();155}156157@Override158public void invalidate() {159ref.clear();160}161162// no need to override equals/hashCode.163}164165// use this message when throwing exceptions166String getPathForExceptionMessage() {167return path;168}169170// use this path for permission checks171String getPathForPermissionCheck() {172return path;173}174175// use this path for Win32 calls176// This method will prefix long paths with \\?\ or \\?\UNC as required.177String getPathForWin32Calls() throws WindowsException {178// short absolute paths can be used directly179if (isAbsolute() && path.length() <= MAX_PATH)180return path;181182// return cached values if available183WeakReference<String> ref = pathForWin32Calls;184String resolved = (ref != null) ? ref.get() : null;185if (resolved != null) {186// Win32 path already available187return resolved;188}189190// resolve against default directory191resolved = getAbsolutePath();192193// Long paths need to have "." and ".." removed and be prefixed with194// "\\?\". Note that it is okay to remove ".." even when it follows195// a link - for example, it is okay for foo/link/../bar to be changed196// to foo/bar. The reason is that Win32 APIs to access foo/link/../bar197// will access foo/bar anyway (which differs to Unix systems)198if (resolved.length() > MAX_PATH) {199if (resolved.length() > MAX_LONG_PATH) {200throw new WindowsException("Cannot access file with path exceeding "201+ MAX_LONG_PATH + " characters");202}203resolved = addPrefixIfNeeded(GetFullPathName(resolved));204}205206// cache the resolved path (except drive relative paths as the working207// directory on removal media devices can change during the lifetime208// of the VM)209if (type != WindowsPathType.DRIVE_RELATIVE) {210synchronized (path) {211pathForWin32Calls = new WeakReference<String>(resolved);212}213}214return resolved;215}216217// return this path resolved against the file system's default directory218private String getAbsolutePath() throws WindowsException {219if (isAbsolute())220return path;221222// Relative path ("foo" for example)223if (type == WindowsPathType.RELATIVE) {224String defaultDirectory = getFileSystem().defaultDirectory();225if (isEmpty())226return defaultDirectory;227if (defaultDirectory.endsWith("\\")) {228return defaultDirectory + path;229} else {230StringBuilder sb =231new StringBuilder(defaultDirectory.length() + path.length() + 1);232return sb.append(defaultDirectory).append('\\').append(path).toString();233}234}235236// Directory relative path ("\foo" for example)237if (type == WindowsPathType.DIRECTORY_RELATIVE) {238String defaultRoot = getFileSystem().defaultRoot();239return defaultRoot + path.substring(1);240}241242// Drive relative path ("C:foo" for example).243if (isSameDrive(root, getFileSystem().defaultRoot())) {244// relative to default directory245String remaining = path.substring(root.length());246String defaultDirectory = getFileSystem().defaultDirectory();247String result;248if (defaultDirectory.endsWith("\\")) {249result = defaultDirectory + remaining;250} else {251result = defaultDirectory + "\\" + remaining;252}253return result;254} else {255// relative to some other drive256String wd;257try {258int dt = GetDriveType(root + "\\");259if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)260throw new WindowsException("");261wd = GetFullPathName(root + ".");262} catch (WindowsException x) {263throw new WindowsException("Unable to get working directory of drive '" +264Character.toUpperCase(root.charAt(0)) + "'");265}266String result = wd;267if (wd.endsWith("\\")) {268result += path.substring(root.length());269} else {270if (path.length() > root.length())271result += "\\" + path.substring(root.length());272}273return result;274}275}276277// returns true if same drive letter278private static boolean isSameDrive(String root1, String root2) {279return Character.toUpperCase(root1.charAt(0)) ==280Character.toUpperCase(root2.charAt(0));281}282283// Add long path prefix to path if required284static String addPrefixIfNeeded(String path) {285if (path.length() > MAX_PATH) {286if (path.startsWith("\\\\")) {287path = "\\\\?\\UNC" + path.substring(1, path.length());288} else {289path = "\\\\?\\" + path;290}291}292return path;293}294295@Override296public WindowsFileSystem getFileSystem() {297return fs;298}299300// -- Path operations --301302private boolean isEmpty() {303return path.length() == 0;304}305306private WindowsPath emptyPath() {307return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");308}309310@Override311public Path getFileName() {312int len = path.length();313// represents empty path314if (len == 0)315return this;316// represents root component only317if (root.length() == len)318return null;319int off = path.lastIndexOf('\\');320if (off < root.length())321off = root.length();322else323off++;324return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));325}326327@Override328public WindowsPath getParent() {329// represents root component only330if (root.length() == path.length())331return null;332int off = path.lastIndexOf('\\');333if (off < root.length())334return getRoot();335else336return new WindowsPath(getFileSystem(),337type,338root,339path.substring(0, off));340}341342@Override343public WindowsPath getRoot() {344if (root.length() == 0)345return null;346return new WindowsPath(getFileSystem(), type, root, root);347}348349// package-private350WindowsPathType type() {351return type;352}353354// package-private355boolean isUnc() {356return type == WindowsPathType.UNC;357}358359boolean needsSlashWhenResolving() {360if (path.endsWith("\\"))361return false;362return path.length() > root.length();363}364365@Override366public boolean isAbsolute() {367return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;368}369370static WindowsPath toWindowsPath(Path path) {371if (path == null)372throw new NullPointerException();373if (!(path instanceof WindowsPath)) {374throw new ProviderMismatchException();375}376return (WindowsPath)path;377}378379@Override380public WindowsPath relativize(Path obj) {381WindowsPath other = toWindowsPath(obj);382if (this.equals(other))383return emptyPath();384385// can only relativize paths of the same type386if (this.type != other.type)387throw new IllegalArgumentException("'other' is different type of Path");388389// can only relativize paths if root component matches390if (!this.root.equalsIgnoreCase(other.root))391throw new IllegalArgumentException("'other' has different root");392393int bn = this.getNameCount();394int cn = other.getNameCount();395396// skip matching names397int n = (bn > cn) ? cn : bn;398int i = 0;399while (i < n) {400if (!this.getName(i).equals(other.getName(i)))401break;402i++;403}404405// append ..\ for remaining names in the base406StringBuilder result = new StringBuilder();407for (int j=i; j<bn; j++) {408result.append("..\\");409}410411// append remaining names in child412for (int j=i; j<cn; j++) {413result.append(other.getName(j).toString());414result.append("\\");415}416417// drop trailing slash in result418result.setLength(result.length()-1);419return createFromNormalizedPath(getFileSystem(), result.toString());420}421422@Override423public Path normalize() {424final int count = getNameCount();425if (count == 0 || isEmpty())426return this;427428boolean[] ignore = new boolean[count]; // true => ignore name429int remaining = count; // number of names remaining430431// multiple passes to eliminate all occurrences of "." and "name/.."432int prevRemaining;433do {434prevRemaining = remaining;435int prevName = -1;436for (int i=0; i<count; i++) {437if (ignore[i])438continue;439440String name = elementAsString(i);441442// not "." or ".."443if (name.length() > 2) {444prevName = i;445continue;446}447448// "." or something else449if (name.length() == 1) {450// ignore "."451if (name.charAt(0) == '.') {452ignore[i] = true;453remaining--;454} else {455prevName = i;456}457continue;458}459460// not ".."461if (name.charAt(0) != '.' || name.charAt(1) != '.') {462prevName = i;463continue;464}465466// ".." found467if (prevName >= 0) {468// name/<ignored>/.. found so mark name and ".." to be469// ignored470ignore[prevName] = true;471ignore[i] = true;472remaining = remaining - 2;473prevName = -1;474} else {475// Cases:476// C:\<ignored>\..477// \\server\\share\<ignored>\..478// \<ignored>..479if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {480boolean hasPrevious = false;481for (int j=0; j<i; j++) {482if (!ignore[j]) {483hasPrevious = true;484break;485}486}487if (!hasPrevious) {488// all proceeding names are ignored489ignore[i] = true;490remaining--;491}492}493}494}495} while (prevRemaining > remaining);496497// no redundant names498if (remaining == count)499return this;500501// corner case - all names removed502if (remaining == 0) {503return (root.length() == 0) ? emptyPath() : getRoot();504}505506// re-constitute the path from the remaining names.507StringBuilder result = new StringBuilder();508if (root != null)509result.append(root);510for (int i=0; i<count; i++) {511if (!ignore[i]) {512result.append(getName(i));513result.append("\\");514}515}516517// drop trailing slash in result518result.setLength(result.length()-1);519return createFromNormalizedPath(getFileSystem(), result.toString());520}521522@Override523public WindowsPath resolve(Path obj) {524WindowsPath other = toWindowsPath(obj);525if (other.isEmpty())526return this;527if (other.isAbsolute())528return other;529530switch (other.type) {531case RELATIVE: {532String result;533if (path.endsWith("\\") || (root.length() == path.length())) {534result = path + other.path;535} else {536result = path + "\\" + other.path;537}538return new WindowsPath(getFileSystem(), type, root, result);539}540541case DIRECTORY_RELATIVE: {542String result;543if (root.endsWith("\\")) {544result = root + other.path.substring(1);545} else {546result = root + other.path;547}548return createFromNormalizedPath(getFileSystem(), result);549}550551case DRIVE_RELATIVE: {552if (!root.endsWith("\\"))553return other;554// if different roots then return other555String thisRoot = root.substring(0, root.length()-1);556if (!thisRoot.equalsIgnoreCase(other.root))557return other;558// same roots559String remaining = other.path.substring(other.root.length());560String result;561if (path.endsWith("\\")) {562result = path + remaining;563} else {564result = path + "\\" + remaining;565}566return createFromNormalizedPath(getFileSystem(), result);567}568569default:570throw new AssertionError();571}572}573574// generate offset array575private void initOffsets() {576if (offsets == null) {577ArrayList<Integer> list = new ArrayList<>();578if (isEmpty()) {579// empty path considered to have one name element580list.add(0);581} else {582int start = root.length();583int off = root.length();584while (off < path.length()) {585if (path.charAt(off) != '\\') {586off++;587} else {588list.add(start);589start = ++off;590}591}592if (start != off)593list.add(start);594}595synchronized (this) {596if (offsets == null)597offsets = list.toArray(new Integer[list.size()]);598}599}600}601602@Override603public int getNameCount() {604initOffsets();605return offsets.length;606}607608private String elementAsString(int i) {609initOffsets();610if (i == (offsets.length-1))611return path.substring(offsets[i]);612return path.substring(offsets[i], offsets[i+1]-1);613}614615@Override616public WindowsPath getName(int index) {617initOffsets();618if (index < 0 || index >= offsets.length)619throw new IllegalArgumentException();620return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));621}622623@Override624public WindowsPath subpath(int beginIndex, int endIndex) {625initOffsets();626if (beginIndex < 0)627throw new IllegalArgumentException();628if (beginIndex >= offsets.length)629throw new IllegalArgumentException();630if (endIndex > offsets.length)631throw new IllegalArgumentException();632if (beginIndex >= endIndex)633throw new IllegalArgumentException();634635StringBuilder sb = new StringBuilder();636Integer[] nelems = new Integer[endIndex - beginIndex];637for (int i = beginIndex; i < endIndex; i++) {638nelems[i-beginIndex] = sb.length();639sb.append(elementAsString(i));640if (i != (endIndex-1))641sb.append("\\");642}643return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());644}645646@Override647public boolean startsWith(Path obj) {648if (!(Objects.requireNonNull(obj) instanceof WindowsPath))649return false;650WindowsPath other = (WindowsPath)obj;651652// if this path has a root component the given path's root must match653if (!this.root.equalsIgnoreCase(other.root)) {654return false;655}656657// empty path starts with itself658if (other.isEmpty())659return this.isEmpty();660661// roots match so compare elements662int thisCount = getNameCount();663int otherCount = other.getNameCount();664if (otherCount <= thisCount) {665while (--otherCount >= 0) {666String thisElement = this.elementAsString(otherCount);667String otherElement = other.elementAsString(otherCount);668// FIXME: should compare in uppercase669if (!thisElement.equalsIgnoreCase(otherElement))670return false;671}672return true;673}674return false;675}676677@Override678public boolean endsWith(Path obj) {679if (!(Objects.requireNonNull(obj) instanceof WindowsPath))680return false;681WindowsPath other = (WindowsPath)obj;682683// other path is longer684if (other.path.length() > this.path.length()) {685return false;686}687688// empty path ends in itself689if (other.isEmpty()) {690return this.isEmpty();691}692693int thisCount = this.getNameCount();694int otherCount = other.getNameCount();695696// given path has more elements that this path697if (otherCount > thisCount) {698return false;699}700701// compare roots702if (other.root.length() > 0) {703if (otherCount < thisCount)704return false;705// FIXME: should compare in uppercase706if (!this.root.equalsIgnoreCase(other.root))707return false;708}709710// match last 'otherCount' elements711int off = thisCount - otherCount;712while (--otherCount >= 0) {713String thisElement = this.elementAsString(off + otherCount);714String otherElement = other.elementAsString(otherCount);715// FIXME: should compare in uppercase716if (!thisElement.equalsIgnoreCase(otherElement))717return false;718}719return true;720}721722@Override723public int compareTo(Path obj) {724if (obj == null)725throw new NullPointerException();726String s1 = path;727String s2 = ((WindowsPath)obj).path;728int n1 = s1.length();729int n2 = s2.length();730int min = Math.min(n1, n2);731for (int i = 0; i < min; i++) {732char c1 = s1.charAt(i);733char c2 = s2.charAt(i);734if (c1 != c2) {735c1 = Character.toUpperCase(c1);736c2 = Character.toUpperCase(c2);737if (c1 != c2) {738return c1 - c2;739}740}741}742return n1 - n2;743}744745@Override746public boolean equals(Object obj) {747if ((obj != null) && (obj instanceof WindowsPath)) {748return compareTo((Path)obj) == 0;749}750return false;751}752753@Override754public int hashCode() {755// OK if two or more threads compute hash756int h = hash;757if (h == 0) {758for (int i = 0; i< path.length(); i++) {759h = 31*h + Character.toUpperCase(path.charAt(i));760}761hash = h;762}763return h;764}765766@Override767public String toString() {768return path;769}770771// -- file operations --772773// package-private774long openForReadAttributeAccess(boolean followLinks)775throws WindowsException776{777int flags = FILE_FLAG_BACKUP_SEMANTICS;778if (!followLinks && getFileSystem().supportsLinks())779flags |= FILE_FLAG_OPEN_REPARSE_POINT;780return CreateFile(getPathForWin32Calls(),781FILE_READ_ATTRIBUTES,782(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),7830L,784OPEN_EXISTING,785flags);786}787788void checkRead() {789SecurityManager sm = System.getSecurityManager();790if (sm != null) {791sm.checkRead(getPathForPermissionCheck());792}793}794795void checkWrite() {796SecurityManager sm = System.getSecurityManager();797if (sm != null) {798sm.checkWrite(getPathForPermissionCheck());799}800}801802void checkDelete() {803SecurityManager sm = System.getSecurityManager();804if (sm != null) {805sm.checkDelete(getPathForPermissionCheck());806}807}808809@Override810public URI toUri() {811return WindowsUriSupport.toUri(this);812}813814@Override815public WindowsPath toAbsolutePath() {816if (isAbsolute())817return this;818819// permission check as per spec820SecurityManager sm = System.getSecurityManager();821if (sm != null) {822sm.checkPropertyAccess("user.dir");823}824825try {826return createFromNormalizedPath(getFileSystem(), getAbsolutePath());827} catch (WindowsException x) {828throw new IOError(new IOException(x.getMessage()));829}830}831832@Override833public WindowsPath toRealPath(LinkOption... options) throws IOException {834checkRead();835String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options));836return createFromNormalizedPath(getFileSystem(), rp);837}838839@Override840public WatchKey register(WatchService watcher,841WatchEvent.Kind<?>[] events,842WatchEvent.Modifier... modifiers)843throws IOException844{845if (watcher == null)846throw new NullPointerException();847if (!(watcher instanceof WindowsWatchService))848throw new ProviderMismatchException();849850// When a security manager is set then we need to make a defensive851// copy of the modifiers and check for the Windows specific FILE_TREE852// modifier. When the modifier is present then check that permission853// has been granted recursively.854SecurityManager sm = System.getSecurityManager();855if (sm != null) {856boolean watchSubtree = false;857final int ml = modifiers.length;858if (ml > 0) {859modifiers = Arrays.copyOf(modifiers, ml);860int i=0;861while (i < ml) {862if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) {863watchSubtree = true;864break;865}866}867}868String s = getPathForPermissionCheck();869sm.checkRead(s);870if (watchSubtree)871sm.checkRead(s + "\\-");872}873874return ((WindowsWatchService)watcher).register(this, events, modifiers);875}876}877878879