Skip to content

Commit

Permalink
os: support app store symbolic links
Browse files Browse the repository at this point in the history
IO_REPARSE_TAG_APPEXECLINK are types of links that are created when
installing an application via the Windows App Store. The location
of these links is added to the Windows PATH and the system can
resolve them. This adds support for these links so go can resolve them.

Fixes golang#42919
  • Loading branch information
JanDeDobbeleer committed Apr 19, 2022
1 parent aa8262d commit aaf7044
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/internal/syscall/windows/reparse_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ package windows

import (
"syscall"
"unicode/utf16"
"unsafe"
)

const (
FSCTL_SET_REPARSE_POINT = 0x000900A4
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
IO_REPARSE_TAG_APPEXECLINK = 0x8000001B

SYMLINK_FLAG_RELATIVE = 1
)
Expand Down Expand Up @@ -88,3 +90,30 @@ func (rb *MountPointReparseBuffer) Path() string {
n2 := (rb.SubstituteNameOffset + rb.SubstituteNameLength) / 2
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))[n1:n2:n2])
}

// AppExecLinkReparseBuffer is described at
// https://stackoverflow.com/a/65583702
// It appears there is no official Microsoft
// documentation for AppExecLinkReparseBuffer yet.
type AppExecLinkReparseBuffer struct {
Version uint32
StringList [1]uint16
}

// Path returns the path stored in rb.
// The buffer contains 4 null terminated strings inside
// StringList. The third string contains the path to the
// executable
func (rb *AppExecLinkReparseBuffer) Path() string {
for j, from, i, p := 0, 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(&rb.StringList[0])); true; i++ {
if p[i] == 0 {
// return 3rd string
if j == 2 {
return string(utf16.Decode(p[from:i]))
}
j++
from = i + 1
}
}
return ""
}
3 changes: 3 additions & 0 deletions src/os/exec/lp_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func findExecutable(file string, exts []string) (string, error) {
return f, nil
}
}
if file, err := os.Readlink(file); err == nil {
return file, nil
}
return "", fs.ErrNotExist
}

Expand Down
10 changes: 10 additions & 0 deletions src/os/file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,16 @@ func readlink(path string) (string, error) {
return normaliseLinkPath(s)
case windows.IO_REPARSE_TAG_MOUNT_POINT:
return normaliseLinkPath((*windows.MountPointReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME)).Path())
case windows.IO_REPARSE_TAG_APPEXECLINK:
appExecLink := (*windows.AppExecLinkReparseBuffer)(unsafe.Pointer(&rdb.DUMMYUNIONNAME))
if appExecLink.Version != 3 {
return " ", errors.New("unknown AppExecLink version")
}
s := appExecLink.Path()
if s == "" {
return "", errors.New("unable to retrieve AppExecLink from buffer")
}
return normaliseLinkPath(s)
default:
// the path is not a symlink or junction but another type of reparse
// point
Expand Down

0 comments on commit aaf7044

Please sign in to comment.