diff --git a/src/java.base/windows/classes/java/io/WinNTFileSystem.java b/src/java.base/windows/classes/java/io/WinNTFileSystem.java index 5e0ec76112b..e700d44cfae 100644 --- a/src/java.base/windows/classes/java/io/WinNTFileSystem.java +++ b/src/java.base/windows/classes/java/io/WinNTFileSystem.java @@ -41,6 +41,8 @@ import sun.security.action.GetPropertyAction; */ final class WinNTFileSystem extends FileSystem { + private static final String LONG_PATH_PREFIX = "\\\\?\\"; + private final char slash; private final char altSlash; private final char semicolon; @@ -60,6 +62,25 @@ final class WinNTFileSystem extends FileSystem { } } + // Strip a long path or UNC prefix and return the result. + // If there is no such prefix, return the parameter passed in. + private static String stripLongOrUNCPrefix(String path) { + // if a prefix is present, remove it + if (path.startsWith(LONG_PATH_PREFIX)) { + if (path.startsWith("UNC\\", 4)) { + path = "\\\\" + path.substring(8); + } else { + path = path.substring(4); + // if only "UNC" remains, a trailing "\\" was likely removed + if (path.equals("UNC")) { + path = "\\\\"; + } + } + } + + return path; + } + WinNTFileSystem() { Properties props = GetPropertyAction.privilegedGetProperties(); slash = props.getProperty("file.separator").charAt(0); @@ -98,6 +119,7 @@ final class WinNTFileSystem extends FileSystem { This way we iterate through the whole pathname string only once. */ @Override public String normalize(String path) { + path = stripLongOrUNCPrefix(path); int n = path.length(); char slash = this.slash; char altSlash = this.altSlash; @@ -223,6 +245,8 @@ final class WinNTFileSystem extends FileSystem { @Override public int prefixLength(String path) { + assert !path.startsWith(LONG_PATH_PREFIX); + char slash = this.slash; int n = path.length(); if (n == 0) return 0; @@ -242,6 +266,8 @@ final class WinNTFileSystem extends FileSystem { @Override public String resolve(String parent, String child) { + assert !child.startsWith(LONG_PATH_PREFIX); + int pn = parent.length(); if (pn == 0) return child; int cn = child.length(); @@ -320,6 +346,9 @@ final class WinNTFileSystem extends FileSystem { @Override public boolean isAbsolute(File f) { + String path = f.getPath(); + assert !path.startsWith(LONG_PATH_PREFIX); + int pl = f.getPrefixLength(); return (((pl == 2) && (f.getPath().charAt(0) == slash)) || (pl == 3)); @@ -358,6 +387,8 @@ final class WinNTFileSystem extends FileSystem { @Override public String resolve(File f) { String path = f.getPath(); + assert !path.startsWith(LONG_PATH_PREFIX); + int pl = f.getPrefixLength(); if ((pl == 2) && (path.charAt(0) == slash)) return path; /* UNC */ @@ -440,6 +471,8 @@ final class WinNTFileSystem extends FileSystem { @Override public String canonicalize(String path) throws IOException { + assert !path.startsWith(LONG_PATH_PREFIX); + // If path is a drive letter only then skip canonicalization int len = path.length(); if ((len == 2) && diff --git a/test/jdk/java/io/File/GetAbsolutePath.java b/test/jdk/java/io/File/GetAbsolutePath.java index 1f69d0b6eb3..09356f64840 100644 --- a/test/jdk/java/io/File/GetAbsolutePath.java +++ b/test/jdk/java/io/File/GetAbsolutePath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, 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 @@ -22,74 +22,89 @@ */ /* @test - @bug 4131169 4109131 - @summary Basic test for getAbsolutePath method + * @bug 4131169 4109131 8287843 + * @summary Basic test for getAbsolutePath method + * @run junit GetAbsolutePath */ -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; public class GetAbsolutePath { - private static boolean ignoreCase = false; + private static final String USER_DIR = System.getProperty("user.dir"); - private static void ck(String path, String ans) throws Exception { - File f = new File(path); - String p = f.getAbsolutePath(); - if ((ignoreCase && p.equalsIgnoreCase(ans)) || p.equals(ans)) - System.err.println(path + " ==> " + p); - else - throw new Exception(path + ": expected " + ans + ", got " + p); + private static char driveLetter() { + assert System.getProperty("os.name").startsWith("Windows"); + + if ((USER_DIR.length() > 2) && (USER_DIR.charAt(1) == ':') + && (USER_DIR.charAt(2) == '\\')) + return USER_DIR.charAt(0); + + throw new RuntimeException("Current directory has no drive"); } - private static void testWin32() throws Exception { - String wd = System.getProperty("user.dir"); - char d; - if ((wd.length() > 2) && (wd.charAt(1) == ':') - && (wd.charAt(2) == '\\')) - d = wd.charAt(0); - else - throw new Exception("Current directory has no drive"); - ck("/foo/bar", d + ":\\foo\\bar"); - ck("\\foo\\bar", d + ":\\foo\\bar"); - ck("c:\\foo\\bar", "c:\\foo\\bar"); - ck("c:/foo/bar", "c:\\foo\\bar"); - ck("\\\\foo\\bar", "\\\\foo\\bar"); + private static Stream windowsSource() { + char drive = driveLetter(); + return Stream.of(Arguments.of("/foo/bar", drive + ":\\foo\\bar"), + Arguments.of("\\foo\\bar", drive + ":\\foo\\bar"), + Arguments.of("c:\\foo\\bar", "c:\\foo\\bar"), + Arguments.of("c:/foo/bar", "c:\\foo\\bar"), + Arguments.of("\\\\foo\\bar", "\\\\foo\\bar"), + Arguments.of("", USER_DIR), // empty path + Arguments.of("\\\\?\\foo", USER_DIR + "\\foo"), + Arguments.of("\\\\?\\C:\\Users\\x", "C:\\Users\\x"), + Arguments.of("\\\\?\\" + drive + ":", USER_DIR), + Arguments.of("\\\\?\\" + drive + ":bar", USER_DIR + "\\bar")); + } - /* Tricky directory-relative case */ - d = Character.toLowerCase(d); + @EnabledOnOs(OS.WINDOWS) + @ParameterizedTest + @MethodSource("windowsSource") + public void windows(String path, String absolute) throws IOException { + File file = new File(path); + assertEquals(0, absolute.compareToIgnoreCase(file.getAbsolutePath())); + } + + @EnabledOnOs(OS.WINDOWS) + @Test + public void windowsDriveRelative() throws IOException { + // Tricky directory-relative case + char d = Character.toLowerCase(driveLetter()); char z = 0; if (d != 'c') z = 'c'; else if (d != 'd') z = 'd'; if (z != 0) { File f = new File(z + ":."); if (f.exists()) { - String zwd = f.getCanonicalPath(); - ck(z + ":foo", zwd + "\\foo"); + String zUSER_DIR = f.getCanonicalPath(); + assertEquals(z + ":foo", zUSER_DIR + "\\foo"); } } - - /* Empty path */ - ck("", wd); } - private static void testUnix() throws Exception { - String wd = System.getProperty("user.dir"); - ck("foo", wd + "/foo"); - ck("foo/bar", wd + "/foo/bar"); - ck("/foo", "/foo"); - ck("/foo/bar", "/foo/bar"); - - /* Empty path */ - ck("", wd); + private static Stream unixSource() { + return Stream.of(Arguments.of("foo", USER_DIR + "/foo"), + Arguments.of("foo/bar", USER_DIR + "/foo/bar"), + Arguments.of("/foo", "/foo"), + Arguments.of("/foo/bar", "/foo/bar"), + Arguments.of("", USER_DIR)); } - public static void main(String[] args) throws Exception { - if (File.separatorChar == '\\') { - ignoreCase = true; - testWin32(); - } - if (File.separatorChar == '/') testUnix(); + @EnabledOnOs({OS.LINUX, OS.MAC}) + @ParameterizedTest + @MethodSource("unixSource") + public void unix(String path, String absolute) throws IOException { + assertEquals(absolute, new File(path).getAbsolutePath()); } - } diff --git a/test/jdk/java/io/File/GetCanonicalPath.java b/test/jdk/java/io/File/GetCanonicalPath.java index dfd0fa5caf9..0abf25244d2 100644 --- a/test/jdk/java/io/File/GetCanonicalPath.java +++ b/test/jdk/java/io/File/GetCanonicalPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -22,21 +22,81 @@ */ /* @test - @bug 4899022 - @summary Look for erroneous representation of drive letter + * @bug 4899022 + * @requires (os.family == "windows") + * @summary Look for erroneous representation of drive letter + * @run junit GetCanonicalPath */ -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; public class GetCanonicalPath { - public static void main(String[] args) throws Exception { - if (File.separatorChar == '\\') { - testDriveLetter(); - } + private static Stream pathProvider() { + List list = new ArrayList(); + + File dir = new File(System.getProperty("user.dir", ".")); + char drive = dir.getPath().charAt(0); + + String pathname = drive + ":\\"; + list.add(Arguments.of(pathname, pathname)); + + list.add(Arguments.of(drive + ":", dir.toString())); + + String name = "foo"; + pathname = "\\\\?\\" + name; + list.add(Arguments.of(pathname, new File(dir, name).toString())); + pathname = "\\\\?\\" + drive + ":" + name; + list.add(Arguments.of(pathname, new File(dir, name).toString())); + + pathname = "foo\\bar\\gus"; + list.add(Arguments.of(pathname, new File(dir, pathname).toString())); + + pathname = drive + ":\\foo\\bar\\gus"; + list.add(Arguments.of(pathname, pathname)); + + pathname = "\\\\server\\share\\foo\\bar\\gus"; + list.add(Arguments.of(pathname, pathname)); + + pathname = "\\\\localhost\\" + drive + "$\\Users\\file.dat"; + list.add(Arguments.of(pathname, pathname)); + + list.add(Arguments.of("\\\\?\\" + drive + ":\\Users\\file.dat", + drive + ":\\Users\\file.dat")); + list.add(Arguments.of("\\\\?\\UNC\\localhost\\" + drive + "$\\Users\\file.dat", + "\\\\localhost\\" + drive + "$\\Users\\file.dat")); + + return list.stream(); } - private static void testDriveLetter() throws Exception { + + @ParameterizedTest + @ValueSource(strings = {"\\\\?", "\\\\?\\UNC", "\\\\?\\UNC\\"}) + void badPaths(String pathname) { + assertThrows(IOException.class, () -> new File(pathname).getCanonicalPath()); + } + + @ParameterizedTest + @MethodSource("pathProvider") + void goodPaths(String pathname, String expected) throws IOException { + File file = new File(pathname); + String canonicalPath = file.getCanonicalPath(); + assertEquals(expected, canonicalPath); + } + + @Test + void driveLetter() throws IOException { String path = new File("c:/").getCanonicalPath(); - if (path.length() > 3) - throw new RuntimeException("Drive letter incorrectly represented"); + assertFalse(path.length() > 3, "Drive letter incorrectly represented"); } } diff --git a/test/jdk/java/io/File/IsAbsolute.java b/test/jdk/java/io/File/IsAbsolute.java index 581d7bebf1d..cdeda917c32 100644 --- a/test/jdk/java/io/File/IsAbsolute.java +++ b/test/jdk/java/io/File/IsAbsolute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -22,42 +22,46 @@ */ /* @test - @bug 4022397 - @summary General test for isAbsolute + * @bug 4022397 8287843 + * @summary General test for isAbsolute + * @run junit IsAbsolute */ -import java.io.*; +import java.io.File; +import java.io.IOException; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.*; public class IsAbsolute { - - private static void ck(String path, boolean ans) throws Exception { - File f = new File(path); - boolean x = f.isAbsolute(); - if (x != ans) - throw new Exception(path + ": expected " + ans + ", got " + x); - System.err.println(path + " ==> " + x); + @EnabledOnOs(OS.WINDOWS) + @ParameterizedTest + @ValueSource(strings = {"c:\\foo\\bar", "c:/foo/bar", "\\\\foo\\bar"}) + public void windowsAbsolute(String path) throws IOException { + assertTrue(new File(path).isAbsolute()); } - private static void testWin32() throws Exception { - ck("/foo/bar", false); - ck("\\foo\\bar", false); - ck("c:\\foo\\bar", true); - ck("c:/foo/bar", true); - ck("c:foo\\bar", false); - ck("\\\\foo\\bar", true); + @EnabledOnOs(OS.WINDOWS) + @ParameterizedTest + @ValueSource(strings = {"/foo/bar", "\\foo\\bar", "c:foo\\bar"}) + public void windowsNotAbsolute(String path) throws IOException { + assertFalse(new File(path).isAbsolute()); } - private static void testUnix() throws Exception { - ck("foo", false); - ck("foo/bar", false); - ck("/foo", true); - ck("/foo/bar", true); + @EnabledOnOs({OS.LINUX, OS.MAC}) + @ParameterizedTest + @ValueSource(strings = {"/foo", "/foo/bar"}) + public void unixAbsolute(String path) throws IOException { + assertTrue(new File(path).isAbsolute()); } - public static void main(String[] args) throws Exception { - if (File.separatorChar == '\\') testWin32(); - if (File.separatorChar == '/') testUnix(); + @EnabledOnOs({OS.LINUX, OS.MAC}) + @ParameterizedTest + @ValueSource(strings = {"foo", "foo/bar"}) + public void unixNotAbsolute(String path) throws IOException { + assertFalse(new File(path).isAbsolute()); } - } diff --git a/test/jdk/java/io/File/WindowsPrefixes.java b/test/jdk/java/io/File/WindowsPrefixes.java new file mode 100644 index 00000000000..0997f1f45cc --- /dev/null +++ b/test/jdk/java/io/File/WindowsPrefixes.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1998, 2023, 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. + * + * 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. + */ + +/* @test + * @bug 8287843 + * @summary Basic test for Windows path prefixes + * @requires (os.family == "windows") + * @run junit WindowsPrefixes + */ + +import java.io.File; +import java.io.IOException; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.*; + +public class WindowsPrefixes { + + private static Stream paths() { + return Stream.of(Arguments.of(""), + Arguments.of("C:\\"), + Arguments.of("C:"), + Arguments.of("\\foo"), + Arguments.of("foo"), + Arguments.of("foo\\bar"), + Arguments.of("C:\\foo"), + Arguments.of("C:foo"), + Arguments.of("C:\\foo\\bar")); + } + + @ParameterizedTest + @MethodSource("paths") + public void getAbsolutePath(String path) throws IOException { + File file = new File(path); + File that = new File("\\\\?\\" + path); + assertEquals(file.getAbsolutePath(), that.getAbsolutePath()); + } + + @ParameterizedTest + @MethodSource("paths") + public void getCanonicalPath(String path) throws IOException { + File file = new File(path); + File that = new File("\\\\?\\" + path); + assertEquals(file.getCanonicalPath(), that.getCanonicalPath()); + } + + @ParameterizedTest + @MethodSource("paths") + public void getName(String path) throws IOException { + File file = new File(path); + File that = new File("\\\\?\\" + path); + assertEquals(file.getName(), that.getName()); + } + + @ParameterizedTest + @MethodSource("paths") + public void getParent(String path) throws IOException { + File file = new File(path); + File that = new File("\\\\?\\" + path); + assertEquals(file.getParent(), that.getParent()); + } + + @ParameterizedTest + @MethodSource("paths") + public void isAbsolute(String path) throws IOException { + File file = new File(path); + File that = new File("\\\\?\\" + path); + assertEquals(file.isAbsolute(), that.isAbsolute()); + } +}