8003887: File.getCanonicalFile() does not resolve symlinks on MS Windows
Reviewed-by: alanb
This commit is contained in:
parent
b389bb4567
commit
042053c3a8
@ -490,12 +490,26 @@ final class WinNTFileSystem extends FileSystem {
|
||||
return path;
|
||||
return "" + ((char) (c-32)) + ':' + '\\';
|
||||
}
|
||||
return canonicalize0(path);
|
||||
String canonicalPath = canonicalize0(path);
|
||||
String finalPath = null;
|
||||
try {
|
||||
finalPath = getFinalPath(canonicalPath);
|
||||
} catch (IOException ignored) {
|
||||
finalPath = canonicalPath;
|
||||
}
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
private native String canonicalize0(String path)
|
||||
throws IOException;
|
||||
|
||||
private String getFinalPath(String path) throws IOException {
|
||||
return getFinalPath0(path);
|
||||
}
|
||||
|
||||
private native String getFinalPath0(String path)
|
||||
throws IOException;
|
||||
|
||||
|
||||
/* -- Attribute accessors -- */
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2024, 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
|
||||
@ -46,31 +46,15 @@ static struct {
|
||||
jfieldID path;
|
||||
} ids;
|
||||
|
||||
/**
|
||||
* GetFinalPathNameByHandle is available on Windows Vista and newer
|
||||
*/
|
||||
typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);
|
||||
static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls)
|
||||
{
|
||||
HMODULE handle;
|
||||
jclass fileClass;
|
||||
|
||||
fileClass = (*env)->FindClass(env, "java/io/File");
|
||||
CHECK_NULL(fileClass);
|
||||
ids.path = (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;");
|
||||
CHECK_NULL(ids.path);
|
||||
|
||||
// GetFinalPathNameByHandle requires Windows Vista or newer
|
||||
if (GetModuleHandleExW((GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT),
|
||||
(LPCWSTR)&CreateFileW, &handle) != 0)
|
||||
{
|
||||
GetFinalPathNameByHandle_func = (GetFinalPathNameByHandleProc)
|
||||
GetProcAddress(handle, "GetFinalPathNameByHandleW");
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Path operations -- */
|
||||
@ -88,10 +72,6 @@ static WCHAR* getFinalPath(JNIEnv *env, const WCHAR *path)
|
||||
WCHAR *result;
|
||||
DWORD error;
|
||||
|
||||
/* Need Windows Vista or newer to get the final path */
|
||||
if (GetFinalPathNameByHandle_func == NULL)
|
||||
return NULL;
|
||||
|
||||
h = CreateFileW(path,
|
||||
FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_DELETE |
|
||||
@ -109,13 +89,13 @@ static WCHAR* getFinalPath(JNIEnv *env, const WCHAR *path)
|
||||
*/
|
||||
result = (WCHAR*)malloc(MAX_PATH * sizeof(WCHAR));
|
||||
if (result != NULL) {
|
||||
DWORD len = (*GetFinalPathNameByHandle_func)(h, result, MAX_PATH, 0);
|
||||
DWORD len = GetFinalPathNameByHandleW(h, result, MAX_PATH, 0);
|
||||
if (len >= MAX_PATH) {
|
||||
/* retry with a buffer of the right size */
|
||||
WCHAR* newResult = (WCHAR*)realloc(result, (len+1) * sizeof(WCHAR));
|
||||
if (newResult != NULL) {
|
||||
result = newResult;
|
||||
len = (*GetFinalPathNameByHandle_func)(h, result, len, 0);
|
||||
len = GetFinalPathNameByHandleW(h, result, len, 0);
|
||||
} else {
|
||||
len = 0;
|
||||
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
||||
@ -351,6 +331,25 @@ Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
|
||||
return rv;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_java_io_WinNTFileSystem_getFinalPath0(JNIEnv* env, jobject this, jstring pathname) {
|
||||
jstring rv = NULL;
|
||||
|
||||
WITH_UNICODE_STRING(env, pathname, path) {
|
||||
WCHAR* finalPath = getFinalPath(env, path);
|
||||
if (finalPath != NULL) {
|
||||
rv = (*env)->NewString(env, finalPath, (jsize)wcslen(finalPath));
|
||||
free(finalPath);
|
||||
}
|
||||
} END_UNICODE_STRING(env, path);
|
||||
|
||||
if (rv == NULL && !(*env)->ExceptionCheck(env)) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* -- Attribute accessors -- */
|
||||
|
||||
/* Check whether or not the file name in "path" is a Windows reserved
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2024, 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,17 +22,21 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 4899022
|
||||
* @bug 4899022 8003887
|
||||
* @summary Look for erroneous representation of drive letter
|
||||
* @run junit GetCanonicalPath
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
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;
|
||||
@ -122,4 +126,88 @@ public class GetCanonicalPath {
|
||||
String path = new File("c:/").getCanonicalPath();
|
||||
assertFalse(path.length() > 3, "Drive letter incorrectly represented");
|
||||
}
|
||||
|
||||
// Create a File with the given pathname and return the File as a Path
|
||||
private static Path createFile(String pathname) throws IOException {
|
||||
File file = new File(pathname);
|
||||
file.deleteOnExit();
|
||||
return file.toPath();
|
||||
}
|
||||
|
||||
private static boolean supportsLinks = true;
|
||||
private static String linkMessage;
|
||||
|
||||
private static Path link;
|
||||
private static Path sublink;
|
||||
private static Path subsub;
|
||||
|
||||
@BeforeAll
|
||||
static void createSymlinks() throws IOException {
|
||||
final String DIR = "dir";
|
||||
final String SUBDIR = "subdir";
|
||||
final String TARGET = "target.txt";
|
||||
final String LINK = "link";
|
||||
final String SUBLINK = "sublink";
|
||||
final String FILE = "file.txt";
|
||||
|
||||
// Create directories dir/subdir
|
||||
Path dir = createFile(DIR);
|
||||
Path subdir = createFile(dir.resolve(SUBDIR).toString());
|
||||
Files.createDirectories(subdir);
|
||||
|
||||
// Create file dir/subdir/target.txt
|
||||
Path target = createFile(subdir.resolve(TARGET).toString());
|
||||
Files.createFile(target);
|
||||
|
||||
// Create symbolic link link -> dir
|
||||
link = createFile(Path.of(LINK).toString());
|
||||
try {
|
||||
Files.createSymbolicLink(link, dir);
|
||||
} catch (UnsupportedOperationException | IOException x) {
|
||||
if (OS.WINDOWS.isCurrentOs()) {
|
||||
supportsLinks = false;
|
||||
linkMessage = "\"" + x.getMessage() + "\"";
|
||||
return;
|
||||
} else {
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
|
||||
sublink = createFile(Path.of(DIR, SUBDIR, SUBLINK).toString());
|
||||
Path file = createFile(Path.of(DIR, SUBDIR, FILE).toString());
|
||||
Files.createFile(file);
|
||||
|
||||
// Create symbolic link dir/subdir/sublink -> file.txt
|
||||
Files.createSymbolicLink(sublink, Path.of(FILE));
|
||||
sublink.toFile().deleteOnExit();
|
||||
|
||||
subsub = createFile(Path.of(LINK, SUBDIR, SUBLINK).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkToDir() throws IOException {
|
||||
Assumptions.assumeTrue(supportsLinks, linkMessage);
|
||||
|
||||
// Check link evaluates to dir
|
||||
assertEquals(link.toRealPath().toString(),
|
||||
link.toFile().getCanonicalPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkToFile() throws IOException {
|
||||
Assumptions.assumeTrue(supportsLinks, linkMessage);
|
||||
|
||||
// Check sublink evaluates to file.txt
|
||||
assertEquals(sublink.toRealPath().toString(),
|
||||
sublink.toFile().getCanonicalPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkToFileInSubdir() throws IOException {
|
||||
Assumptions.assumeTrue(supportsLinks, linkMessage);
|
||||
|
||||
// Check link/subdir/sublink evaluates to dir/subdir/file.txt
|
||||
assertEquals(subsub.toRealPath().toString(),
|
||||
subsub.toFile().getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user