Skip to content

Commit 8852b23

Browse files
committed
Kotlin: Resolve substed drives on Windows
1 parent 8b2ab6e commit 8852b23

2 files changed

Lines changed: 82 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: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
break;
28+
} catch (RuntimeException | UnsatisfiedLinkError ignored) {
29+
}
30+
}
31+
}
32+
available = loaded;
33+
}
34+
35+
private SubstResolver() {}
36+
37+
/**
38+
* Given a drive root like {@code "X:\\"} (or {@code "X:/"}), returns the path that drive is
39+
* {@code subst}ed to, or {@code null} if the drive root was not subst drive or if an internal
40+
* error occurred.
41+
*/
42+
private static native String nativeResolveSubst(String driveRoot);
43+
44+
/**
45+
* If {@code f} is an absolute path starting with a {@code subst}ed drive letter, return an
46+
* equivalent path with the drive letter replaced by its target. Otherwise return {@code f}
47+
* unchanged.
48+
*/
49+
public static File resolve(File f) {
50+
if (!available) {
51+
return f;
52+
}
53+
String path = f.getPath();
54+
if (path.length() < 3 || path.charAt(1) != ':') {
55+
return f;
56+
}
57+
char sep = path.charAt(2);
58+
if (sep != '\\' && sep != '/') {
59+
return f;
60+
}
61+
if (!isDriveLetter(path.charAt(0))) {
62+
return f;
63+
}
64+
65+
String resolved = nativeResolveSubst(path.substring(0, 3));
66+
if (resolved == null) {
67+
return f;
68+
}
69+
70+
// Append the remainder of the original path. The native side strips away
71+
// any trailing separator.
72+
return new File(resolved + path.substring(2));
73+
}
74+
75+
private static boolean isDriveLetter(char c) {
76+
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
77+
}
78+
}

0 commit comments

Comments
 (0)