From 7b8344490c9ba629f4f8c92be7554fc78adb5071 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 9 Oct 2025 10:11:41 -0700 Subject: [PATCH 1/2] 8368907: Windows Error code 1314 not translated to AccessDeniedException (win) --- .../unix/classes/sun/nio/fs/#UnixPath.java# | 1035 +++++++++++++++++ .../classes/sun/nio/fs/WindowsConstants.java | 1 + .../classes/sun/nio/fs/WindowsException.java | 35 +- 3 files changed, 1056 insertions(+), 15 deletions(-) create mode 100644 src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# diff --git a/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# b/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# new file mode 100644 index 0000000000000..4aba2187577e2 --- /dev/null +++ b/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2008, 2025, 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.io.IOException; +import java.net.URI; +import java.nio.charset.CharacterCodingException; +import java.nio.file.DirectoryStream; +import java.nio.file.InvalidPathException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.ProviderMismatchException; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.Arrays; +import java.util.Objects; + +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; +import jdk.internal.util.ArraysSupport; +import jdk.internal.vm.annotation.Stable; + +import static sun.nio.fs.UnixConstants.*; +import static sun.nio.fs.UnixNativeDispatcher.*; + +/** + * Linux/Mac implementation of java.nio.file.Path + */ +class UnixPath implements Path { + + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + private final UnixFileSystem fs; + + // internal representation + private final byte[] path; + + // String representation (created lazily, no need to be volatile) + private @Stable String stringValue; + + // cached hashcode (created lazily, no need to be volatile) + private int hash; + + // array of offsets of elements in path (created lazily) + private volatile int[] offsets; + + UnixPath(UnixFileSystem fs, byte[] path) { + this.fs = fs; + this.path = path; + } + + UnixPath(UnixFileSystem fs, String input) { + // removes redundant slashes and checks for invalid characters + this(fs, encode(fs, normalizeAndCheck(input))); + } + + // package-private + // removes redundant slashes and check input for invalid characters + static String normalizeAndCheck(String input) { + int n = input.length(); + char prevChar = 0; + for (int i=0; i < n; i++) { + char c = input.charAt(i); + if ((c == '/') && (prevChar == '/')) + return normalize(input, n, i - 1); + checkNotNul(input, c); + prevChar = c; + } + if (prevChar == '/' && n > 1) { + return input.substring(0, n - 1); + } + return input; + } + + private static void checkNotNul(String input, char c) { + if (c == '\u0000') + throw new InvalidPathException(input, "Nul character not allowed"); + } + + private static String normalize(String input, int len, int off) { + if (len == 0) + return input; + int n = len; + while ((n > 0) && (input.charAt(n - 1) == '/')) n--; + if (n == 0) + return "/"; + StringBuilder sb = new StringBuilder(input.length()); + if (off > 0) + sb.append(input, 0, off); + char prevChar = 0; + for (int i=off; i < n; i++) { + char c = input.charAt(i); + if ((c == '/') && (prevChar == '/')) + continue; + checkNotNul(input, c); + sb.append(c); + prevChar = c; + } + return sb.toString(); + } + + // encodes the given path-string into a sequence of bytes + private static byte[] encode(UnixFileSystem fs, String input) { + try { + return JLA.uncheckedGetBytesOrThrow(input, Util.jnuEncoding()); + } catch (CharacterCodingException cce) { + throw new InvalidPathException(input, + "Malformed input or input contains unmappable characters"); + } + } + + // package-private + byte[] asByteArray() { + return path; + } + + // use this path when making system/library calls + byte[] getByteArrayForSysCalls() { + // resolve against default directory if required (chdir allowed or + // file system default directory is not working directory) + if (getFileSystem().needToResolveAgainstDefaultDirectory()) { + return resolve(getFileSystem().defaultDirectory(), path); + } else { + if (!isEmpty()) { + return path; + } else { + // empty path case will access current directory + byte[] here = { '.' }; + return here; + } + } + } + + // use this message when throwing exceptions + String getPathForExceptionMessage() { + return toString(); + } + + // use this path for permission checks + String getPathForPermissionCheck() { + if (getFileSystem().needToResolveAgainstDefaultDirectory()) { + return Util.toString(getByteArrayForSysCalls()); + } else { + return toString(); + } + } + + // Checks that the given file is a UnixPath + static UnixPath toUnixPath(Path obj) { + if (obj == null) + throw new NullPointerException(); + if (!(obj instanceof UnixPath)) + throw new ProviderMismatchException(); + return (UnixPath)obj; + } + + // create offset list if not already created + private void initOffsets() { + if (offsets == null) { + int count, index; + + // count names + count = 0; + index = 0; + if (isEmpty()) { + // empty path has one name + count = 1; + } else { + while (index < path.length) { + byte c = path[index++]; + if (c != '/') { + count++; + while (index < path.length && path[index] != '/') + index++; + } + } + } + + // populate offsets + int[] result = new int[count]; + count = 0; + index = 0; + while (index < path.length) { + byte c = path[index]; + if (c == '/') { + index++; + } else { + result[count++] = index++; + while (index < path.length && path[index] != '/') + index++; + } + } + synchronized (this) { + if (offsets == null) + offsets = result; + } + } + } + + // returns {@code true} if this path is an empty path + boolean isEmpty() { + return path.length == 0; + } + + // returns an empty path + private UnixPath emptyPath() { + return new UnixPath(getFileSystem(), new byte[0]); + } + + + // return true if this path has "." or ".." + private boolean hasDotOrDotDot() { + int n = getNameCount(); + for (int i=0; i 0 && path[0] == '/') { + return getFileSystem().rootDirectory(); + } else { + return null; + } + } + + @Override + public UnixPath getFileName() { + initOffsets(); + + int count = offsets.length; + + // no elements so no name + if (count == 0) + return null; + + // one name element and no root component + if (count == 1 && path.length > 0 && path[0] != '/') + return this; + + int lastOffset = offsets[count-1]; + int len = path.length - lastOffset; + byte[] result = new byte[len]; + System.arraycopy(path, lastOffset, result, 0, len); + return new UnixPath(getFileSystem(), result); + } + + @Override + public UnixPath getParent() { + initOffsets(); + + int count = offsets.length; + if (count == 0) { + // no elements so no parent + return null; + } + int len = offsets[count-1] - 1; + if (len <= 0) { + // parent is root only (may be null) + return getRoot(); + } + byte[] result = new byte[len]; + System.arraycopy(path, 0, result, 0, len); + return new UnixPath(getFileSystem(), result); + } + + @Override + public int getNameCount() { + initOffsets(); + return offsets.length; + } + + @Override + public UnixPath getName(int index) { + initOffsets(); + if (index < 0) + throw new IllegalArgumentException(); + if (index >= offsets.length) + throw new IllegalArgumentException(); + + int begin = offsets[index]; + int len; + if (index == (offsets.length-1)) { + len = path.length - begin; + } else { + len = offsets[index+1] - begin - 1; + } + + // construct result + byte[] result = new byte[len]; + System.arraycopy(path, begin, result, 0, len); + return new UnixPath(getFileSystem(), result); + } + + @Override + public UnixPath subpath(int beginIndex, int endIndex) { + initOffsets(); + + if (beginIndex < 0) + throw new IllegalArgumentException(); + if (beginIndex >= offsets.length) + throw new IllegalArgumentException(); + if (endIndex > offsets.length) + throw new IllegalArgumentException(); + if (beginIndex >= endIndex) { + throw new IllegalArgumentException(); + } + + // starting offset and length + int begin = offsets[beginIndex]; + int len; + if (endIndex == offsets.length) { + len = path.length - begin; + } else { + len = offsets[endIndex] - begin - 1; + } + + // construct result + byte[] result = new byte[len]; + System.arraycopy(path, begin, result, 0, len); + return new UnixPath(getFileSystem(), result); + } + + @Override + public boolean isAbsolute() { + return (path.length > 0 && path[0] == '/'); + } + + // Resolve child against given base + private static byte[] resolve(byte[] base, byte[] child) { + int baseLength = base.length; + int childLength = child.length; + if (childLength == 0) + return base; + if (baseLength == 0 || child[0] == '/') + return child; + byte[] result; + if (baseLength == 1 && base[0] == '/') { + result = new byte[childLength + 1]; + result[0] = '/'; + System.arraycopy(child, 0, result, 1, childLength); + } else { + result = new byte[baseLength + 1 + childLength]; + System.arraycopy(base, 0, result, 0, baseLength); + result[base.length] = '/'; + System.arraycopy(child, 0, result, baseLength+1, childLength); + } + return result; + } + + @Override + public UnixPath resolve(Path obj) { + byte[] other = toUnixPath(obj).path; + if (other.length > 0 && other[0] == '/') + return ((UnixPath)obj); + byte[] result = resolve(path, other); + return new UnixPath(getFileSystem(), result); + } + + UnixPath resolve(byte[] other) { + return resolve(new UnixPath(getFileSystem(), other)); + } + + private static final byte[] resolve(byte[] base, byte[]... children) { + // 'start' is either zero, indicating the base, or indicates which + // child is that last one which is an absolute path + int start = 0; + int resultLength = base.length; + + // Locate the last child which is an absolute path and calculate + // the total number of bytes in the resolved path + final int count = children.length; + if (count > 0) { + for (int i = 0; i < count; i++) { + byte[] b = children[i]; + if (b.length > 0) { + if (b[0] == '/') { + start = i + 1; + resultLength = b.length; + } else { + if (resultLength > 0) + resultLength++; + resultLength += b.length; + } + } + } + } + + // If the base is not being superseded by a child which is an + // absolute path, then if at least one child is non-empty and + // the base consists only of a '/', then decrement resultLength to + // account for an extra '/' added in the resultLength computation. + if (start == 0 && resultLength > base.length && base.length == 1 && base[0] == '/') + resultLength--; + + // Allocate the result array and return if empty. + byte[] result = new byte[resultLength]; + if (result.length == 0) + return result; + + // Prepend the base if it is non-empty and would not later be + // overwritten by an absolute child + int offset = 0; + if (start == 0 && base.length > 0) { + System.arraycopy(base, 0, result, 0, base.length); + offset += base.length; + } + + // Append children starting with the last one which is an + // absolute path + if (count > 0) { + int idx = Math.max(0, start - 1); + for (int i = idx; i < count; i++) { + byte[] b = children[i]; + if (b.length > 0) { + if (offset > 0 && result[offset - 1] != '/') + result[offset++] = '/'; + System.arraycopy(b, 0, result, offset, b.length); + offset += b.length; + } + } + } + + return result; + } + + @Override + public UnixPath resolve(Path first, Path... more) { + if (more.length == 0) + return resolve(first); + + byte[][] children = new byte[1 + more.length][]; + children[0] = toUnixPath(first).path; + for (int i = 0; i < more.length; i++) + children[i + 1] = toUnixPath(more[i]).path; + + byte[] result = resolve(path, children); + return new UnixPath(getFileSystem(), result); + } + + @Override + public UnixPath relativize(Path obj) { + UnixPath child = toUnixPath(obj); + if (child.equals(this)) + return emptyPath(); + + // can only relativize paths of the same type + if (this.isAbsolute() != child.isAbsolute()) + throw new IllegalArgumentException("'other' is different type of Path"); + + // this path is the empty path + if (this.isEmpty()) + return child; + + UnixPath base = this; + if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) { + base = base.normalize(); + child = child.normalize(); + } + + int baseCount = base.getNameCount(); + int childCount = child.getNameCount(); + + // skip matching names + int n = Math.min(baseCount, childCount); + int i = 0; + while (i < n) { + if (!base.getName(i).equals(child.getName(i))) + break; + i++; + } + + // remaining elements in child + UnixPath childRemaining; + boolean isChildEmpty; + if (i == childCount) { + childRemaining = emptyPath(); + isChildEmpty = true; + } else { + childRemaining = child.subpath(i, childCount); + isChildEmpty = childRemaining.isEmpty(); + } + + // matched all of base + if (i == baseCount) { + return childRemaining; + } + + // the remainder of base cannot contain ".." + UnixPath baseRemaining = base.subpath(i, baseCount); + if (baseRemaining.hasDotOrDotDot()) { + throw new IllegalArgumentException("Unable to compute relative " + + " path from " + this + " to " + obj); + } + if (baseRemaining.isEmpty()) + return childRemaining; + + // number of ".." needed + int dotdots = baseRemaining.getNameCount(); + if (dotdots == 0) { + return childRemaining; + } + + // result is a "../" for each remaining name in base followed by the + // remaining names in child. If the remainder is the empty path + // then we don't add the final trailing slash. + int len = dotdots*3 + childRemaining.path.length; + if (isChildEmpty) { + assert childRemaining.isEmpty(); + len--; + } + byte[] result = new byte[len]; + int pos = 0; + while (dotdots > 0) { + result[pos++] = (byte)'.'; + result[pos++] = (byte)'.'; + if (isChildEmpty) { + if (dotdots > 1) result[pos++] = (byte)'/'; + } else { + result[pos++] = (byte)'/'; + } + dotdots--; + } + System.arraycopy(childRemaining.path,0, result, pos, + childRemaining.path.length); + return new UnixPath(getFileSystem(), result); + } + + @Override + public UnixPath normalize() { + final int count = getNameCount(); + if (count == 0 || isEmpty()) + return this; + + boolean[] ignore = new boolean[count]; // true => ignore name + int[] size = new int[count]; // length of name + int remaining = count; // number of names remaining + boolean hasDotDot = false; // has at least one .. + boolean isAbsolute = isAbsolute(); + + // first pass: + // 1. compute length of names + // 2. mark all occurrences of "." to ignore + // 3. and look for any occurrences of ".." + for (int i=0; i= 0) { + // name//.. found so mark name and ".." to be + // ignored + ignore[prevName] = true; + ignore[i] = true; + remaining = remaining - 2; + prevName = -1; + } else { + // Case: //.. so mark ".." as ignored + if (isAbsolute) { + boolean hasPrevious = false; + for (int j=0; j remaining); + } + + // no redundant names + if (remaining == count) + return this; + + // corner case - all names removed + if (remaining == 0) { + return isAbsolute ? getFileSystem().rootDirectory() : emptyPath(); + } + + // compute length of result + int len = remaining - 1; + if (isAbsolute) + len++; + + for (int i=0; i 0) { + result[pos++] = '/'; + } + } + } + return new UnixPath(getFileSystem(), result); + } + + @Override + public boolean startsWith(Path other) { + if (!(Objects.requireNonNull(other) instanceof UnixPath)) + return false; + UnixPath that = (UnixPath)other; + + // other path is longer + if (that.path.length > path.length) + return false; + + int thisOffsetCount = getNameCount(); + int thatOffsetCount = that.getNameCount(); + + // other path has no name elements + if (thatOffsetCount == 0 && this.isAbsolute()) { + return that.isEmpty() ? false : true; + } + + // given path has more elements that this path + if (thatOffsetCount > thisOffsetCount) + return false; + + // same number of elements so must be exact match + if ((thatOffsetCount == thisOffsetCount) && + (path.length != that.path.length)) { + return false; + } + + // check offsets of elements match + for (int i=0; i thisLen) + return false; + + // other path is the empty path + if (thisLen > 0 && thatLen == 0) + return false; + + // other path is absolute so this path must be absolute + if (that.isAbsolute() && !this.isAbsolute()) + return false; + + int thisOffsetCount = getNameCount(); + int thatOffsetCount = that.getNameCount(); + + // given path has more elements that this path + if (thatOffsetCount > thisOffsetCount) { + return false; + } else { + // same number of elements + if (thatOffsetCount == thisOffsetCount) { + if (thisOffsetCount == 0) + return true; + int expectedLen = thisLen; + if (this.isAbsolute() && !that.isAbsolute()) + expectedLen--; + if (thatLen != expectedLen) + return false; + } else { + // this path has more elements so given path must be relative + if (that.isAbsolute()) + return false; + } + } + + // compare bytes + int thisPos = offsets[thisOffsetCount - thatOffsetCount]; + int thatPos = that.offsets[0]; + return Arrays.equals(this.path, thisPos, thisLen, that.path, thatPos, thatLen); + } + + @Override + public int compareTo(Path other) { + return Arrays.compareUnsigned(path, ((UnixPath) other).path); + } + + @Override + public boolean equals(Object ob) { + return ob instanceof UnixPath p && compareTo(p) == 0; + } + + @Override + public int hashCode() { + // OK if two or more threads compute hash + int h = hash; + if (h == 0) { + h = ArraysSupport.hashCodeOfUnsigned(path, 0, path.length, 0); + hash = h; + } + return h; + } + + @Override + public String toString() { + // OK if two or more threads create a String + String stringValue = this.stringValue; + if (stringValue == null) { + this.stringValue = stringValue = Util.toString(path); // platform encoding + } + return stringValue; + } + + // -- file operations -- + + // package-private + int openForAttributeAccess(boolean followLinks) throws UnixException { + int flags = O_RDONLY; + if (!followLinks) { + if (O_NOFOLLOW == 0) + throw new UnixException + ("NOFOLLOW_LINKS is not supported on this platform"); + flags |= O_NOFOLLOW; + } + return open(this, flags, 0); + } + + @Override + public UnixPath toAbsolutePath() { + if (isAbsolute()) { + return this; + } + // The path is relative so need to resolve against default directory + return new UnixPath(getFileSystem(), + resolve(getFileSystem().defaultDirectory(), path)); + } + + @Override + public Path toRealPath(LinkOption... options) throws IOException { + UnixPath absolute = toAbsolutePath(); + + // if resolving links then use realpath + if (Util.followLinks(options)) { + try { + byte[] rp = realpath(absolute); + return new UnixPath(getFileSystem(), rp); + } catch (UnixException x) { + x.rethrowAsIOException(this); + } + } + + // if not resolving links then eliminate "." and also ".." + // where the previous element is neither a link nor "..". + // if there is a preceding "..", then it might have followed + // a link or a link followed by a sequence of two or more "..". + // if for example one has the path "link/../../file", + // then if a preceding ".." were eliminated, then the result + // would be "/link/file" instead of the correct + // "/link/../../file". + UnixPath result = fs.rootDirectory(); + boolean parentIsDotDot = false; + for (int i = 0; i < absolute.getNameCount(); i++) { + UnixPath element = absolute.getName(i); + + // eliminate "." + if ((element.asByteArray().length == 1) && + (element.asByteArray()[0] == '.')) + continue; + + // cannot eliminate ".." if previous element is a link or ".." + if ((element.asByteArray().length == 2) && + (element.asByteArray()[0] == '.') && + (element.asByteArray()[1] == '.')) + { + UnixFileAttributes attrs = null; + try { + attrs = UnixFileAttributes.get(result, false); + } catch (UnixException x) { + x.rethrowAsIOException(result); + } + if (!attrs.isSymbolicLink() && !parentIsDotDot) { + result = result.getParent(); + if (result == null) { + result = fs.rootDirectory(); + } + continue; + } + parentIsDotDot = true; + } else { + parentIsDotDot = false; + } + result = result.resolve(element); + } + + // check whether file exists (without following links) + try { + UnixFileAttributes.get(result, false); + } catch (UnixException x) { + x.rethrowAsIOException(result); + } + + // Return if the file system is not both case insensitive and retentive + if (!fs.isCaseInsensitiveAndPreserving()) + return result; + + UnixPath path = fs.rootDirectory(); + + // Traverse the result obtained above from the root downward, leaving + // any '..' elements intact, and replacing other elements with the + // entry in the same directory which has an equal key + for (int i = 0; i < result.getNameCount(); i++ ) { + UnixPath element = result.getName(i); + + // If the element is "..", append it directly and continue + if (element.toString().equals("..")) { + path = path.resolve(element); + continue; + } + + // Derive full path to element and check readability + UnixPath elementPath = path.resolve(element); + + // Obtain the file key of elementPath + UnixFileAttributes attrs = null; + try { + attrs = UnixFileAttributes.get(elementPath, false); + } catch (UnixException x) { + x.rethrowAsIOException(result); + } + final UnixFileKey elementKey = attrs.fileKey(); + + // Obtain the directory stream pointer. It will be closed by + // UnixDirectoryStream::close. + long dp = -1; + try { + dp = opendir(path); + } catch (UnixException x) { + x.rethrowAsIOException(path); + } + + // Obtain the stream of entries in the directory corresponding + // to the path constructed thus far, and extract the entry whose + // internal path bytes equal the internal path bytes of the current + // element, or whose string representation is equal to that of the + // current element ignoring case and whose key is equal to the key + // of the current element + DirectoryStream.Filter filter = (p) -> { return true; }; + String elementName = element.toString(); + try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) { + boolean found = false; + for (Path entry : entries) { + Path name = entry.getFileName(); + if (name.compareTo(element) == 0) { + found = true; + path = path.resolve(entry); + break; + } else if (name.toString().equalsIgnoreCase(elementName)) { + UnixPath p = path.resolve(name); + UnixFileAttributes attributes = null; + try { + attributes = UnixFileAttributes.get(p, false); + UnixFileKey key = attributes.fileKey(); + if (key.equals(elementKey)) { + found = true; + path = path.resolve(entry); + break; + } + }catch (UnixException ignore) { + continue; + } + } + } + + if (!found) { + try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) { + boolean found = false; + for (Path entry : entries) { + Path name = entry.getFileName(); + if (name.compareTo(element) == 0) { + found = true; + path = path.resolve(entry); + break; + } else if (name.toString().equalsIgnoreCase(elementName)) { + UnixPath p = path.resolve(name); + UnixFileAttributes attributes = null; + try { + attributes = UnixFileAttributes.get(p, false); + UnixFileKey key = attributes.fileKey(); + if (key.equals(elementKey)) { + found = true; + path = path.resolve(entry); + break; + } + }catch (UnixException ignore) { + continue; + } + } + } + + if (!found) { + // Fallback which should in theory never happen + path = path.resolve(element); + } + } + } + + return path; + } + + @Override + public URI toUri() { + return UnixUriUtils.toUri(this); + } + + @Override + public WatchKey register(WatchService watcher, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) + throws IOException + { + if (watcher == null) + throw new NullPointerException(); + if (!(watcher instanceof AbstractWatchService)) + throw new ProviderMismatchException(); + return ((AbstractWatchService)watcher).register(this, events, modifiers); + } +} diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java index 47fa16695d267..0c09a80e99e73 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java @@ -96,6 +96,7 @@ private WindowsConstants() { } public static final int ERROR_NOT_SAME_DEVICE = 17; public static final int ERROR_NOT_READY = 21; public static final int ERROR_SHARING_VIOLATION = 32; + public static final int ERROR_NETWORK_ACCESS_DENIED = 65; public static final int ERROR_FILE_EXISTS = 80; public static final int ERROR_INVALID_PARAMETER = 87; public static final int ERROR_DISK_FULL = 112; diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java index f1eff69210b0a..78047227d1033 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, 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 @@ -76,20 +76,25 @@ public Throwable fillInStackTrace() { } private IOException translateToIOException(String file, String other) { - // not created with last error - if (lastError() == 0) - return new IOException(errorString()); - - // handle specific cases - if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND) - return new NoSuchFileException(file, other, null); - if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS) - return new FileAlreadyExistsException(file, other, null); - if (lastError() == ERROR_ACCESS_DENIED) - return new AccessDeniedException(file, other, null); - - // fallback to the more general exception - return new FileSystemException(file, other, errorString()); + IOException ex; + + switch (lastError()) { + // not created with last error + case 0 -> ex = new IOException(errorString()); + + // handle specific cases + case ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND + -> ex = new NoSuchFileException(file, other, null); + case ERROR_FILE_EXISTS, ERROR_ALREADY_EXISTS + -> ex = new FileAlreadyExistsException(file, other, null); + case ERROR_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD + -> ex = new AccessDeniedException(file, other, null); + + // fallback to the more general exception + default -> ex = new FileSystemException(file, other, errorString()); + } + + return ex; } void rethrowAsIOException(String file) throws IOException { From 49e05eb6769bffe3ce71c1f858b52c9bef95f3fe Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Mon, 13 Oct 2025 09:14:39 -0700 Subject: [PATCH 2/2] 8368907: Return switch; remove stray file --- .../unix/classes/sun/nio/fs/#UnixPath.java# | 1035 ----------------- .../classes/sun/nio/fs/WindowsException.java | 18 +- 2 files changed, 7 insertions(+), 1046 deletions(-) delete mode 100644 src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# diff --git a/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# b/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# deleted file mode 100644 index 4aba2187577e2..0000000000000 --- a/src/java.base/unix/classes/sun/nio/fs/#UnixPath.java# +++ /dev/null @@ -1,1035 +0,0 @@ -/* - * Copyright (c) 2008, 2025, 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.io.IOException; -import java.net.URI; -import java.nio.charset.CharacterCodingException; -import java.nio.file.DirectoryStream; -import java.nio.file.InvalidPathException; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.ProviderMismatchException; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.util.Arrays; -import java.util.Objects; - -import jdk.internal.access.JavaLangAccess; -import jdk.internal.access.SharedSecrets; -import jdk.internal.util.ArraysSupport; -import jdk.internal.vm.annotation.Stable; - -import static sun.nio.fs.UnixConstants.*; -import static sun.nio.fs.UnixNativeDispatcher.*; - -/** - * Linux/Mac implementation of java.nio.file.Path - */ -class UnixPath implements Path { - - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - - private final UnixFileSystem fs; - - // internal representation - private final byte[] path; - - // String representation (created lazily, no need to be volatile) - private @Stable String stringValue; - - // cached hashcode (created lazily, no need to be volatile) - private int hash; - - // array of offsets of elements in path (created lazily) - private volatile int[] offsets; - - UnixPath(UnixFileSystem fs, byte[] path) { - this.fs = fs; - this.path = path; - } - - UnixPath(UnixFileSystem fs, String input) { - // removes redundant slashes and checks for invalid characters - this(fs, encode(fs, normalizeAndCheck(input))); - } - - // package-private - // removes redundant slashes and check input for invalid characters - static String normalizeAndCheck(String input) { - int n = input.length(); - char prevChar = 0; - for (int i=0; i < n; i++) { - char c = input.charAt(i); - if ((c == '/') && (prevChar == '/')) - return normalize(input, n, i - 1); - checkNotNul(input, c); - prevChar = c; - } - if (prevChar == '/' && n > 1) { - return input.substring(0, n - 1); - } - return input; - } - - private static void checkNotNul(String input, char c) { - if (c == '\u0000') - throw new InvalidPathException(input, "Nul character not allowed"); - } - - private static String normalize(String input, int len, int off) { - if (len == 0) - return input; - int n = len; - while ((n > 0) && (input.charAt(n - 1) == '/')) n--; - if (n == 0) - return "/"; - StringBuilder sb = new StringBuilder(input.length()); - if (off > 0) - sb.append(input, 0, off); - char prevChar = 0; - for (int i=off; i < n; i++) { - char c = input.charAt(i); - if ((c == '/') && (prevChar == '/')) - continue; - checkNotNul(input, c); - sb.append(c); - prevChar = c; - } - return sb.toString(); - } - - // encodes the given path-string into a sequence of bytes - private static byte[] encode(UnixFileSystem fs, String input) { - try { - return JLA.uncheckedGetBytesOrThrow(input, Util.jnuEncoding()); - } catch (CharacterCodingException cce) { - throw new InvalidPathException(input, - "Malformed input or input contains unmappable characters"); - } - } - - // package-private - byte[] asByteArray() { - return path; - } - - // use this path when making system/library calls - byte[] getByteArrayForSysCalls() { - // resolve against default directory if required (chdir allowed or - // file system default directory is not working directory) - if (getFileSystem().needToResolveAgainstDefaultDirectory()) { - return resolve(getFileSystem().defaultDirectory(), path); - } else { - if (!isEmpty()) { - return path; - } else { - // empty path case will access current directory - byte[] here = { '.' }; - return here; - } - } - } - - // use this message when throwing exceptions - String getPathForExceptionMessage() { - return toString(); - } - - // use this path for permission checks - String getPathForPermissionCheck() { - if (getFileSystem().needToResolveAgainstDefaultDirectory()) { - return Util.toString(getByteArrayForSysCalls()); - } else { - return toString(); - } - } - - // Checks that the given file is a UnixPath - static UnixPath toUnixPath(Path obj) { - if (obj == null) - throw new NullPointerException(); - if (!(obj instanceof UnixPath)) - throw new ProviderMismatchException(); - return (UnixPath)obj; - } - - // create offset list if not already created - private void initOffsets() { - if (offsets == null) { - int count, index; - - // count names - count = 0; - index = 0; - if (isEmpty()) { - // empty path has one name - count = 1; - } else { - while (index < path.length) { - byte c = path[index++]; - if (c != '/') { - count++; - while (index < path.length && path[index] != '/') - index++; - } - } - } - - // populate offsets - int[] result = new int[count]; - count = 0; - index = 0; - while (index < path.length) { - byte c = path[index]; - if (c == '/') { - index++; - } else { - result[count++] = index++; - while (index < path.length && path[index] != '/') - index++; - } - } - synchronized (this) { - if (offsets == null) - offsets = result; - } - } - } - - // returns {@code true} if this path is an empty path - boolean isEmpty() { - return path.length == 0; - } - - // returns an empty path - private UnixPath emptyPath() { - return new UnixPath(getFileSystem(), new byte[0]); - } - - - // return true if this path has "." or ".." - private boolean hasDotOrDotDot() { - int n = getNameCount(); - for (int i=0; i 0 && path[0] == '/') { - return getFileSystem().rootDirectory(); - } else { - return null; - } - } - - @Override - public UnixPath getFileName() { - initOffsets(); - - int count = offsets.length; - - // no elements so no name - if (count == 0) - return null; - - // one name element and no root component - if (count == 1 && path.length > 0 && path[0] != '/') - return this; - - int lastOffset = offsets[count-1]; - int len = path.length - lastOffset; - byte[] result = new byte[len]; - System.arraycopy(path, lastOffset, result, 0, len); - return new UnixPath(getFileSystem(), result); - } - - @Override - public UnixPath getParent() { - initOffsets(); - - int count = offsets.length; - if (count == 0) { - // no elements so no parent - return null; - } - int len = offsets[count-1] - 1; - if (len <= 0) { - // parent is root only (may be null) - return getRoot(); - } - byte[] result = new byte[len]; - System.arraycopy(path, 0, result, 0, len); - return new UnixPath(getFileSystem(), result); - } - - @Override - public int getNameCount() { - initOffsets(); - return offsets.length; - } - - @Override - public UnixPath getName(int index) { - initOffsets(); - if (index < 0) - throw new IllegalArgumentException(); - if (index >= offsets.length) - throw new IllegalArgumentException(); - - int begin = offsets[index]; - int len; - if (index == (offsets.length-1)) { - len = path.length - begin; - } else { - len = offsets[index+1] - begin - 1; - } - - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return new UnixPath(getFileSystem(), result); - } - - @Override - public UnixPath subpath(int beginIndex, int endIndex) { - initOffsets(); - - if (beginIndex < 0) - throw new IllegalArgumentException(); - if (beginIndex >= offsets.length) - throw new IllegalArgumentException(); - if (endIndex > offsets.length) - throw new IllegalArgumentException(); - if (beginIndex >= endIndex) { - throw new IllegalArgumentException(); - } - - // starting offset and length - int begin = offsets[beginIndex]; - int len; - if (endIndex == offsets.length) { - len = path.length - begin; - } else { - len = offsets[endIndex] - begin - 1; - } - - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return new UnixPath(getFileSystem(), result); - } - - @Override - public boolean isAbsolute() { - return (path.length > 0 && path[0] == '/'); - } - - // Resolve child against given base - private static byte[] resolve(byte[] base, byte[] child) { - int baseLength = base.length; - int childLength = child.length; - if (childLength == 0) - return base; - if (baseLength == 0 || child[0] == '/') - return child; - byte[] result; - if (baseLength == 1 && base[0] == '/') { - result = new byte[childLength + 1]; - result[0] = '/'; - System.arraycopy(child, 0, result, 1, childLength); - } else { - result = new byte[baseLength + 1 + childLength]; - System.arraycopy(base, 0, result, 0, baseLength); - result[base.length] = '/'; - System.arraycopy(child, 0, result, baseLength+1, childLength); - } - return result; - } - - @Override - public UnixPath resolve(Path obj) { - byte[] other = toUnixPath(obj).path; - if (other.length > 0 && other[0] == '/') - return ((UnixPath)obj); - byte[] result = resolve(path, other); - return new UnixPath(getFileSystem(), result); - } - - UnixPath resolve(byte[] other) { - return resolve(new UnixPath(getFileSystem(), other)); - } - - private static final byte[] resolve(byte[] base, byte[]... children) { - // 'start' is either zero, indicating the base, or indicates which - // child is that last one which is an absolute path - int start = 0; - int resultLength = base.length; - - // Locate the last child which is an absolute path and calculate - // the total number of bytes in the resolved path - final int count = children.length; - if (count > 0) { - for (int i = 0; i < count; i++) { - byte[] b = children[i]; - if (b.length > 0) { - if (b[0] == '/') { - start = i + 1; - resultLength = b.length; - } else { - if (resultLength > 0) - resultLength++; - resultLength += b.length; - } - } - } - } - - // If the base is not being superseded by a child which is an - // absolute path, then if at least one child is non-empty and - // the base consists only of a '/', then decrement resultLength to - // account for an extra '/' added in the resultLength computation. - if (start == 0 && resultLength > base.length && base.length == 1 && base[0] == '/') - resultLength--; - - // Allocate the result array and return if empty. - byte[] result = new byte[resultLength]; - if (result.length == 0) - return result; - - // Prepend the base if it is non-empty and would not later be - // overwritten by an absolute child - int offset = 0; - if (start == 0 && base.length > 0) { - System.arraycopy(base, 0, result, 0, base.length); - offset += base.length; - } - - // Append children starting with the last one which is an - // absolute path - if (count > 0) { - int idx = Math.max(0, start - 1); - for (int i = idx; i < count; i++) { - byte[] b = children[i]; - if (b.length > 0) { - if (offset > 0 && result[offset - 1] != '/') - result[offset++] = '/'; - System.arraycopy(b, 0, result, offset, b.length); - offset += b.length; - } - } - } - - return result; - } - - @Override - public UnixPath resolve(Path first, Path... more) { - if (more.length == 0) - return resolve(first); - - byte[][] children = new byte[1 + more.length][]; - children[0] = toUnixPath(first).path; - for (int i = 0; i < more.length; i++) - children[i + 1] = toUnixPath(more[i]).path; - - byte[] result = resolve(path, children); - return new UnixPath(getFileSystem(), result); - } - - @Override - public UnixPath relativize(Path obj) { - UnixPath child = toUnixPath(obj); - if (child.equals(this)) - return emptyPath(); - - // can only relativize paths of the same type - if (this.isAbsolute() != child.isAbsolute()) - throw new IllegalArgumentException("'other' is different type of Path"); - - // this path is the empty path - if (this.isEmpty()) - return child; - - UnixPath base = this; - if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) { - base = base.normalize(); - child = child.normalize(); - } - - int baseCount = base.getNameCount(); - int childCount = child.getNameCount(); - - // skip matching names - int n = Math.min(baseCount, childCount); - int i = 0; - while (i < n) { - if (!base.getName(i).equals(child.getName(i))) - break; - i++; - } - - // remaining elements in child - UnixPath childRemaining; - boolean isChildEmpty; - if (i == childCount) { - childRemaining = emptyPath(); - isChildEmpty = true; - } else { - childRemaining = child.subpath(i, childCount); - isChildEmpty = childRemaining.isEmpty(); - } - - // matched all of base - if (i == baseCount) { - return childRemaining; - } - - // the remainder of base cannot contain ".." - UnixPath baseRemaining = base.subpath(i, baseCount); - if (baseRemaining.hasDotOrDotDot()) { - throw new IllegalArgumentException("Unable to compute relative " - + " path from " + this + " to " + obj); - } - if (baseRemaining.isEmpty()) - return childRemaining; - - // number of ".." needed - int dotdots = baseRemaining.getNameCount(); - if (dotdots == 0) { - return childRemaining; - } - - // result is a "../" for each remaining name in base followed by the - // remaining names in child. If the remainder is the empty path - // then we don't add the final trailing slash. - int len = dotdots*3 + childRemaining.path.length; - if (isChildEmpty) { - assert childRemaining.isEmpty(); - len--; - } - byte[] result = new byte[len]; - int pos = 0; - while (dotdots > 0) { - result[pos++] = (byte)'.'; - result[pos++] = (byte)'.'; - if (isChildEmpty) { - if (dotdots > 1) result[pos++] = (byte)'/'; - } else { - result[pos++] = (byte)'/'; - } - dotdots--; - } - System.arraycopy(childRemaining.path,0, result, pos, - childRemaining.path.length); - return new UnixPath(getFileSystem(), result); - } - - @Override - public UnixPath normalize() { - final int count = getNameCount(); - if (count == 0 || isEmpty()) - return this; - - boolean[] ignore = new boolean[count]; // true => ignore name - int[] size = new int[count]; // length of name - int remaining = count; // number of names remaining - boolean hasDotDot = false; // has at least one .. - boolean isAbsolute = isAbsolute(); - - // first pass: - // 1. compute length of names - // 2. mark all occurrences of "." to ignore - // 3. and look for any occurrences of ".." - for (int i=0; i= 0) { - // name//.. found so mark name and ".." to be - // ignored - ignore[prevName] = true; - ignore[i] = true; - remaining = remaining - 2; - prevName = -1; - } else { - // Case: //.. so mark ".." as ignored - if (isAbsolute) { - boolean hasPrevious = false; - for (int j=0; j remaining); - } - - // no redundant names - if (remaining == count) - return this; - - // corner case - all names removed - if (remaining == 0) { - return isAbsolute ? getFileSystem().rootDirectory() : emptyPath(); - } - - // compute length of result - int len = remaining - 1; - if (isAbsolute) - len++; - - for (int i=0; i 0) { - result[pos++] = '/'; - } - } - } - return new UnixPath(getFileSystem(), result); - } - - @Override - public boolean startsWith(Path other) { - if (!(Objects.requireNonNull(other) instanceof UnixPath)) - return false; - UnixPath that = (UnixPath)other; - - // other path is longer - if (that.path.length > path.length) - return false; - - int thisOffsetCount = getNameCount(); - int thatOffsetCount = that.getNameCount(); - - // other path has no name elements - if (thatOffsetCount == 0 && this.isAbsolute()) { - return that.isEmpty() ? false : true; - } - - // given path has more elements that this path - if (thatOffsetCount > thisOffsetCount) - return false; - - // same number of elements so must be exact match - if ((thatOffsetCount == thisOffsetCount) && - (path.length != that.path.length)) { - return false; - } - - // check offsets of elements match - for (int i=0; i thisLen) - return false; - - // other path is the empty path - if (thisLen > 0 && thatLen == 0) - return false; - - // other path is absolute so this path must be absolute - if (that.isAbsolute() && !this.isAbsolute()) - return false; - - int thisOffsetCount = getNameCount(); - int thatOffsetCount = that.getNameCount(); - - // given path has more elements that this path - if (thatOffsetCount > thisOffsetCount) { - return false; - } else { - // same number of elements - if (thatOffsetCount == thisOffsetCount) { - if (thisOffsetCount == 0) - return true; - int expectedLen = thisLen; - if (this.isAbsolute() && !that.isAbsolute()) - expectedLen--; - if (thatLen != expectedLen) - return false; - } else { - // this path has more elements so given path must be relative - if (that.isAbsolute()) - return false; - } - } - - // compare bytes - int thisPos = offsets[thisOffsetCount - thatOffsetCount]; - int thatPos = that.offsets[0]; - return Arrays.equals(this.path, thisPos, thisLen, that.path, thatPos, thatLen); - } - - @Override - public int compareTo(Path other) { - return Arrays.compareUnsigned(path, ((UnixPath) other).path); - } - - @Override - public boolean equals(Object ob) { - return ob instanceof UnixPath p && compareTo(p) == 0; - } - - @Override - public int hashCode() { - // OK if two or more threads compute hash - int h = hash; - if (h == 0) { - h = ArraysSupport.hashCodeOfUnsigned(path, 0, path.length, 0); - hash = h; - } - return h; - } - - @Override - public String toString() { - // OK if two or more threads create a String - String stringValue = this.stringValue; - if (stringValue == null) { - this.stringValue = stringValue = Util.toString(path); // platform encoding - } - return stringValue; - } - - // -- file operations -- - - // package-private - int openForAttributeAccess(boolean followLinks) throws UnixException { - int flags = O_RDONLY; - if (!followLinks) { - if (O_NOFOLLOW == 0) - throw new UnixException - ("NOFOLLOW_LINKS is not supported on this platform"); - flags |= O_NOFOLLOW; - } - return open(this, flags, 0); - } - - @Override - public UnixPath toAbsolutePath() { - if (isAbsolute()) { - return this; - } - // The path is relative so need to resolve against default directory - return new UnixPath(getFileSystem(), - resolve(getFileSystem().defaultDirectory(), path)); - } - - @Override - public Path toRealPath(LinkOption... options) throws IOException { - UnixPath absolute = toAbsolutePath(); - - // if resolving links then use realpath - if (Util.followLinks(options)) { - try { - byte[] rp = realpath(absolute); - return new UnixPath(getFileSystem(), rp); - } catch (UnixException x) { - x.rethrowAsIOException(this); - } - } - - // if not resolving links then eliminate "." and also ".." - // where the previous element is neither a link nor "..". - // if there is a preceding "..", then it might have followed - // a link or a link followed by a sequence of two or more "..". - // if for example one has the path "link/../../file", - // then if a preceding ".." were eliminated, then the result - // would be "/link/file" instead of the correct - // "/link/../../file". - UnixPath result = fs.rootDirectory(); - boolean parentIsDotDot = false; - for (int i = 0; i < absolute.getNameCount(); i++) { - UnixPath element = absolute.getName(i); - - // eliminate "." - if ((element.asByteArray().length == 1) && - (element.asByteArray()[0] == '.')) - continue; - - // cannot eliminate ".." if previous element is a link or ".." - if ((element.asByteArray().length == 2) && - (element.asByteArray()[0] == '.') && - (element.asByteArray()[1] == '.')) - { - UnixFileAttributes attrs = null; - try { - attrs = UnixFileAttributes.get(result, false); - } catch (UnixException x) { - x.rethrowAsIOException(result); - } - if (!attrs.isSymbolicLink() && !parentIsDotDot) { - result = result.getParent(); - if (result == null) { - result = fs.rootDirectory(); - } - continue; - } - parentIsDotDot = true; - } else { - parentIsDotDot = false; - } - result = result.resolve(element); - } - - // check whether file exists (without following links) - try { - UnixFileAttributes.get(result, false); - } catch (UnixException x) { - x.rethrowAsIOException(result); - } - - // Return if the file system is not both case insensitive and retentive - if (!fs.isCaseInsensitiveAndPreserving()) - return result; - - UnixPath path = fs.rootDirectory(); - - // Traverse the result obtained above from the root downward, leaving - // any '..' elements intact, and replacing other elements with the - // entry in the same directory which has an equal key - for (int i = 0; i < result.getNameCount(); i++ ) { - UnixPath element = result.getName(i); - - // If the element is "..", append it directly and continue - if (element.toString().equals("..")) { - path = path.resolve(element); - continue; - } - - // Derive full path to element and check readability - UnixPath elementPath = path.resolve(element); - - // Obtain the file key of elementPath - UnixFileAttributes attrs = null; - try { - attrs = UnixFileAttributes.get(elementPath, false); - } catch (UnixException x) { - x.rethrowAsIOException(result); - } - final UnixFileKey elementKey = attrs.fileKey(); - - // Obtain the directory stream pointer. It will be closed by - // UnixDirectoryStream::close. - long dp = -1; - try { - dp = opendir(path); - } catch (UnixException x) { - x.rethrowAsIOException(path); - } - - // Obtain the stream of entries in the directory corresponding - // to the path constructed thus far, and extract the entry whose - // internal path bytes equal the internal path bytes of the current - // element, or whose string representation is equal to that of the - // current element ignoring case and whose key is equal to the key - // of the current element - DirectoryStream.Filter filter = (p) -> { return true; }; - String elementName = element.toString(); - try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) { - boolean found = false; - for (Path entry : entries) { - Path name = entry.getFileName(); - if (name.compareTo(element) == 0) { - found = true; - path = path.resolve(entry); - break; - } else if (name.toString().equalsIgnoreCase(elementName)) { - UnixPath p = path.resolve(name); - UnixFileAttributes attributes = null; - try { - attributes = UnixFileAttributes.get(p, false); - UnixFileKey key = attributes.fileKey(); - if (key.equals(elementKey)) { - found = true; - path = path.resolve(entry); - break; - } - }catch (UnixException ignore) { - continue; - } - } - } - - if (!found) { - try (DirectoryStream entries = new UnixDirectoryStream(path, dp, filter)) { - boolean found = false; - for (Path entry : entries) { - Path name = entry.getFileName(); - if (name.compareTo(element) == 0) { - found = true; - path = path.resolve(entry); - break; - } else if (name.toString().equalsIgnoreCase(elementName)) { - UnixPath p = path.resolve(name); - UnixFileAttributes attributes = null; - try { - attributes = UnixFileAttributes.get(p, false); - UnixFileKey key = attributes.fileKey(); - if (key.equals(elementKey)) { - found = true; - path = path.resolve(entry); - break; - } - }catch (UnixException ignore) { - continue; - } - } - } - - if (!found) { - // Fallback which should in theory never happen - path = path.resolve(element); - } - } - } - - return path; - } - - @Override - public URI toUri() { - return UnixUriUtils.toUri(this); - } - - @Override - public WatchKey register(WatchService watcher, - WatchEvent.Kind[] events, - WatchEvent.Modifier... modifiers) - throws IOException - { - if (watcher == null) - throw new NullPointerException(); - if (!(watcher instanceof AbstractWatchService)) - throw new ProviderMismatchException(); - return ((AbstractWatchService)watcher).register(this, events, modifiers); - } -} diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java index 78047227d1033..532728f57ed27 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsException.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsException.java @@ -76,25 +76,21 @@ public Throwable fillInStackTrace() { } private IOException translateToIOException(String file, String other) { - IOException ex; - - switch (lastError()) { + return switch (lastError()) { // not created with last error - case 0 -> ex = new IOException(errorString()); + case 0 -> new IOException(errorString()); // handle specific cases case ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND - -> ex = new NoSuchFileException(file, other, null); + -> new NoSuchFileException(file, other, null); case ERROR_FILE_EXISTS, ERROR_ALREADY_EXISTS - -> ex = new FileAlreadyExistsException(file, other, null); + -> new FileAlreadyExistsException(file, other, null); case ERROR_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD - -> ex = new AccessDeniedException(file, other, null); + -> new AccessDeniedException(file, other, null); // fallback to the more general exception - default -> ex = new FileSystemException(file, other, errorString()); - } - - return ex; + default -> new FileSystemException(file, other, errorString()); + }; } void rethrowAsIOException(String file) throws IOException {