Skip to content

Commit 16ef5d3

Browse files
committed
Kotlin: Resolve substed drives on Windows
1 parent 8b2ab6e commit 16ef5d3

2 files changed

Lines changed: 81 additions & 3 deletions

File tree

java/kotlin-extractor/src/main/java/com/semmle/util/files/FileUtil.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,13 +1237,14 @@ public static String relativePathLink (File f, File base)
12371237

12381238
/**
12391239
* Try to convert a file into a canonical file. Handles the possible IO exception by just making
1240-
* the path absolute.
1240+
* the path absolute. Also resolves subst drives on Windows.
12411241
*/
12421242
public static File tryMakeCanonical (File f)
12431243
{
12441244
try {
1245-
return f.getCanonicalFile();
1246-
}
1245+
// getCanonicalFile does not canonicalize subst drives on Windows, so do this separately. This
1246+
// is a no-op on non-Windows platforms.
1247+
return SubstResolver.resolve(f.getCanonicalFile()); }
12471248
catch (IOException ignored) {
12481249
Exceptions.ignore(ignored, "Can't log error: Could be too verbose.");
12491250
return new File(simplifyPath(f));
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.semmle.util.files;
2+
3+
import java.io.File;
4+
import java.net.URISyntaxException;
5+
import java.nio.file.Path;
6+
import java.nio.file.Paths;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
/**
11+
* Resolves Windows {@code subst}ed drive letters to their underlying paths. On non-Windows
12+
* platforms, or when the native library failed to load, resolving will be a no-op.
13+
*/
14+
public class SubstResolver {
15+
private static final boolean available;
16+
17+
static {
18+
boolean loaded = false;
19+
if (File.separatorChar == '\\') {
20+
String dist = System.getenv("CODEQL_DIST");
21+
if (dist != null && !dist.isEmpty()) {
22+
try {
23+
Path library = Paths.get(dist).resolve("tools")
24+
.resolve("win64").resolve("canonicalize.dll").toAbsolutePath();
25+
System.load(library.toString());
26+
loaded = true;
27+
} catch (RuntimeException | UnsatisfiedLinkError ignored) {
28+
}
29+
}
30+
}
31+
available = loaded;
32+
}
33+
34+
private SubstResolver() {}
35+
36+
/**
37+
* Given a drive root like {@code "X:\\"} (or {@code "X:/"}), returns the path that drive is
38+
* {@code subst}ed to, or {@code null} if the drive root was not subst drive or if an internal
39+
* error occurred.
40+
*/
41+
private static native String nativeResolveSubst(String driveRoot);
42+
43+
/**
44+
* If {@code f} is an absolute path starting with a {@code subst}ed drive letter, return an
45+
* equivalent path with the drive letter replaced by its target. Otherwise return {@code f}
46+
* unchanged.
47+
*/
48+
public static File resolve(File f) {
49+
if (!available) {
50+
return f;
51+
}
52+
String path = f.getPath();
53+
if (path.length() < 3 || path.charAt(1) != ':') {
54+
return f;
55+
}
56+
char sep = path.charAt(2);
57+
if (sep != '\\' && sep != '/') {
58+
return f;
59+
}
60+
if (!isDriveLetter(path.charAt(0))) {
61+
return f;
62+
}
63+
64+
String resolved = nativeResolveSubst(path.substring(0, 3));
65+
if (resolved == null) {
66+
return f;
67+
}
68+
69+
// Append the remainder of the original path. The native side strips away
70+
// any trailing separator.
71+
return new File(resolved + path.substring(2));
72+
}
73+
74+
private static boolean isDriveLetter(char c) {
75+
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
76+
}
77+
}

0 commit comments

Comments
 (0)