Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go/extractor/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,9 +764,9 @@ func normalizedPath(ast *ast.File, fset *token.FileSet) string {
file := fset.File(ast.Package).Name()
path, err := filepath.EvalSymlinks(file)
if err != nil {
return file
path = file
}
return path
return util.ResolvePath(path)
}

// extractFile extracts AST information for the given file
Expand Down
5 changes: 4 additions & 1 deletion go/extractor/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ require (
golang.org/x/tools v0.47.0
)

require github.com/stretchr/testify v1.11.1
require (
github.com/stretchr/testify v1.11.1
golang.org/x/sys v0.46.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go/extractor/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
9 changes: 8 additions & 1 deletion go/extractor/util/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions go/extractor/util/subst_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//go:build !windows

package util

// ResolvePath is a no-op on non-Windows platforms.
func ResolvePath(path string) string { return path }
86 changes: 86 additions & 0 deletions go/extractor/util/subst_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//go:build windows

package util

import (
"os"
"path/filepath"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

var (
dll *syscall.DLL
procResolve *syscall.Proc
procFree *syscall.Proc
available bool
)

func init() {
dist := os.Getenv("CODEQL_DIST")
if dist == "" {
return
}
dllPath := filepath.Join(dist, "tools", "win64", "canonicalize.dll")
d, err := syscall.LoadDLL(dllPath)
if err != nil {
return
}
p, err := d.FindProc("resolve_subst_u8")
if err != nil {
return
}
f, _ := d.FindProc("resolve_subst_free_u8")
dll = d
procResolve = p
procFree = f
available = true
}

// ResolvePath resolves subst'd drive letters in a full path.
// If the path starts with a subst'd drive letter, replaces it with the backing path.
// Otherwise returns the path unchanged.
func ResolvePath(path string) string {
if len(path) < 3 {
return path
}
if path[1] != ':' {
return path
}
if path[2] != '\\' && path[2] != '/' {
return path
}
c := path[0]
if !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
return path
}

resolved, ok := resolveDrive(path[:3])
if !ok {
return path
}
return resolved + path[2:]
}

// resolveDrive resolves a subst'd drive root (e.g. "X:\") to its backing path.
// The second return value is false if the drive is not subst'd or on error.
func resolveDrive(driveRoot string) (string, bool) {
if !available {
return "", false
}
driveBytes, err := windows.ByteSliceFromString(driveRoot)
if err != nil {
return "", false
}
ret, _, _ := procResolve.Call(uintptr(unsafe.Pointer(&driveBytes[0])))
if ret == 0 {
return "", false
}
result := windows.BytePtrToString((*byte)(unsafe.Pointer(ret)))
if procFree != nil {
procFree.Call(ret)
}
return result, true
}
Original file line number Diff line number Diff line change
Expand Up @@ -1242,12 +1242,13 @@ public static String relativePathLink (File f, File base)
public static File tryMakeCanonical (File f)
{
try {
return f.getCanonicalFile();
f = f.getCanonicalFile();
}
catch (IOException ignored) {
Exceptions.ignore(ignored, "Can't log error: Could be too verbose.");
return new File(simplifyPath(f));
f = new File(simplifyPath(f));
}
return SubstResolver.resolve(f);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.semmle.util.files;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
* Resolves Windows {@code subst}ed drive letters to their underlying paths. On non-Windows
* platforms, or when the native library failed to load, {@link #resolve(File)} is a no-op that
* returns its argument unchanged.
*/
public class SubstResolver {
private static final boolean available;

static {
boolean loaded = false;
if (File.separatorChar == '\\') {
String dist = System.getenv("CODEQL_DIST");
if (dist != null && !dist.isEmpty()) {
try {
Path library = Paths.get(dist).resolve("tools").resolve("win64")
.resolve("canonicalize.dll").toAbsolutePath();
System.load(library.toString());
loaded = true;
} catch (RuntimeException | UnsatisfiedLinkError ignored) {
}
}
}
available = loaded;
}

private SubstResolver() {}

/**
* Given a drive root like {@code "X:\\"}, returns the path that drive is
* {@code subst}ed to, or {@code null} if the letter isn't a subst mapping.
*/
private static native String nativeResolveSubst(String driveRoot);

/**
* If {@code f} is an absolute path starting with a {@code subst}ed drive letter, return an
* equivalent path with the drive letter replaced by its target. Otherwise return {@code f}
* unchanged.
*/
public static File resolve(File f) {
if (!available) {
return f;
}
String path = f.getPath();
if (path.length() < 3 || path.charAt(1) != ':') {
return f;
}
char sep = path.charAt(2);
if (sep != '\\' && sep != '/') {
return f;
}
if (!isDriveLetter(path.charAt(0))) {
return f;
}

String resolved = nativeResolveSubst(path.substring(0, 3));
if (resolved == null) {
return f;
}

return new File(resolved + path.substring(2));
}

private static boolean isDriveLetter(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}

public static boolean isAvailable() {
return available;
}
}
Loading