mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8368633: (fs) Path.toRealPath(NOFOLLOW_LINKS) very slow on macOS
Reviewed-by: alanb
This commit is contained in:
parent
65b8fe62b4
commit
09e87971e8
@ -948,28 +948,51 @@ class UnixPath implements Path {
|
||||
|
||||
// Obtain the stream of entries in the directory corresponding
|
||||
// to the path constructed thus far, and extract the entry whose
|
||||
// key is equal to the key of the current element
|
||||
// internal path bytes equal the internal path bytes of the current
|
||||
// element, or whose key is equal to the key of the current element
|
||||
boolean found = false;
|
||||
DirectoryStream.Filter<Path> filter = (p) -> { return true; };
|
||||
// compare path bytes until a match is found or no more entries
|
||||
try (DirectoryStream<Path> entries = new UnixDirectoryStream(path, dp, filter)) {
|
||||
boolean found = false;
|
||||
for (Path entry : entries) {
|
||||
UnixPath p = path.resolve(entry.getFileName());
|
||||
UnixFileAttributes attributes = null;
|
||||
try {
|
||||
attributes = UnixFileAttributes.get(p, false);
|
||||
UnixFileKey key = attributes.fileKey();
|
||||
if (key.equals(elementKey)) {
|
||||
path = path.resolve(entry);
|
||||
found = true;
|
||||
break;
|
||||
Path name = entry.getFileName();
|
||||
if (name.compareTo(element) == 0) {
|
||||
found = true;
|
||||
path = path.resolve(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no path match found, compare file keys
|
||||
if (!found) {
|
||||
try {
|
||||
dp = opendir(path);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(path);
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> entries = new UnixDirectoryStream(path, dp, filter)) {
|
||||
for (Path entry : entries) {
|
||||
Path name = entry.getFileName();
|
||||
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;
|
||||
}
|
||||
} catch (UnixException ignore) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback which should in theory never happen
|
||||
if (!found) {
|
||||
// Fallback which should in theory never happen
|
||||
path = path.resolve(element);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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
|
||||
@ -151,18 +151,18 @@ public class ToRealPath {
|
||||
public void noCollapseDots1() throws IOException {
|
||||
Path subPath = DIR.resolve(Path.of("dir", "subdir"));
|
||||
Path sub = Files.createDirectories(subPath);
|
||||
System.out.println("sub: " + sub);
|
||||
System.err.println("sub: " + sub);
|
||||
Files.createSymbolicLink(LINK, sub);
|
||||
System.out.println("LINK: " + LINK + " -> " + sub);
|
||||
System.err.println("LINK: " + LINK + " -> " + sub);
|
||||
Path p = Path.of("..", "..", FILE.getFileName().toString());
|
||||
System.out.println("p: " + p);
|
||||
System.err.println("p: " + p);
|
||||
Path path = LINK.resolve(p);
|
||||
System.out.println("path: " + path);
|
||||
System.err.println("path: " + path);
|
||||
if (Platform.isWindows() && Files.notExists(path)) {
|
||||
Files.createFile(path);
|
||||
extraDeletions.add(path);
|
||||
}
|
||||
System.out.println("no follow: " + path.toRealPath(NOFOLLOW_LINKS));
|
||||
System.err.println("no follow: " + path.toRealPath(NOFOLLOW_LINKS));
|
||||
if (Platform.isWindows())
|
||||
assertTrue(Files.isSameFile(path.toRealPath(NOFOLLOW_LINKS), path));
|
||||
else
|
||||
@ -181,23 +181,23 @@ public class ToRealPath {
|
||||
Path out = Files.createFile(DIR.resolve(Path.of("out.txt")));
|
||||
Path aaa = DIR.resolve(Path.of("aaa"));
|
||||
Files.createSymbolicLink(aaa, sub);
|
||||
System.out.println("aaa: " + aaa + " -> " + sub);
|
||||
System.err.println("aaa: " + aaa + " -> " + sub);
|
||||
Path bbb = DIR.resolve(Path.of("bbb"));
|
||||
Files.createSymbolicLink(bbb, sub);
|
||||
System.out.println("bbb: " + bbb + " -> " + sub);
|
||||
System.err.println("bbb: " + bbb + " -> " + sub);
|
||||
Path p = Path.of("aaa", "..", "..", "bbb", "..", "..", "out.txt");
|
||||
Path path = DIR.resolve(p);
|
||||
System.out.println("path: " + path);
|
||||
System.err.println("path: " + path);
|
||||
if (Platform.isWindows() && Files.notExists(path)) {
|
||||
Files.createFile(path);
|
||||
extraDeletions.add(path);
|
||||
}
|
||||
System.out.println("no follow: " + path.toRealPath(NOFOLLOW_LINKS));
|
||||
System.err.println("no follow: " + path.toRealPath(NOFOLLOW_LINKS));
|
||||
if (Platform.isWindows())
|
||||
assertTrue(Files.isSameFile(path.toRealPath(NOFOLLOW_LINKS), path));
|
||||
else
|
||||
assertEquals(path.toRealPath(NOFOLLOW_LINKS), path);
|
||||
System.out.println(path.toRealPath());
|
||||
System.err.println(path.toRealPath());
|
||||
|
||||
Files.delete(sub);
|
||||
Files.delete(sub.getParent());
|
||||
|
||||
129
test/micro/org/openjdk/bench/java/nio/file/ToRealPath.java
Normal file
129
test/micro/org/openjdk/bench/java/nio/file/ToRealPath.java
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
* 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 org.openjdk.bench.java.nio.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Random;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public class ToRealPath {
|
||||
|
||||
static Random RND = new Random(17_000_126);
|
||||
|
||||
static final String NAME = "RealPath";
|
||||
static final int LEN = NAME.length();
|
||||
|
||||
Path root;
|
||||
Path[] files;
|
||||
|
||||
@Setup
|
||||
public void init() throws IOException {
|
||||
// root the test files at CWD/NAME
|
||||
root = Path.of(System.getProperty("user.dir")).resolve(NAME);
|
||||
|
||||
// populate files array
|
||||
StringBuilder sb = new StringBuilder();
|
||||
files = new Path[100];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
// create directories up to a depth of 9, inclusive
|
||||
sb.setLength(0);
|
||||
int depth = RND.nextInt(10);
|
||||
for (int j = 0; j < depth; j++) {
|
||||
sb.append("dir");
|
||||
sb.append(j);
|
||||
sb.append(File.separatorChar);
|
||||
}
|
||||
Path dir = root.resolve(sb.toString());
|
||||
Files.createDirectories(dir);
|
||||
|
||||
// set the file prefix with random case conversion
|
||||
String prefix;
|
||||
if (RND.nextBoolean()) {
|
||||
sb.setLength(0);
|
||||
for (int k = 0; k < LEN; k++) {
|
||||
char c = NAME.charAt(k);
|
||||
sb.append(RND.nextBoolean()
|
||||
? Character.toLowerCase(c)
|
||||
: Character.toUpperCase(c));
|
||||
}
|
||||
prefix = sb.append(i).toString();
|
||||
} else {
|
||||
prefix = NAME + i;
|
||||
}
|
||||
|
||||
// create the file
|
||||
Path tmpFile = Files.createTempFile(dir, prefix, ".tmp");
|
||||
|
||||
// set the array path to a version with a lower case name
|
||||
String tmpName = tmpFile.getFileName().toString().toLowerCase();
|
||||
files[i] = tmpFile.getParent().resolve(tmpName);
|
||||
}
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void cleanup() throws IOException {
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file,
|
||||
BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir,
|
||||
IOException e)
|
||||
throws IOException
|
||||
{
|
||||
if (e == null) {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
// directory iteration failed
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Path noFollowLinks() throws IOException {
|
||||
int i = RND.nextInt(0, files.length);
|
||||
return files[i].toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user