package syscallx import ( "syscall" "unsafe" ) type reparseDataBuffer struct { ReparseTag uint32 ReparseDataLength uint16 Reserved uint16 // GenericReparseBuffer reparseBuffer byte } type mountPointReparseBuffer struct { SubstituteNameOffset uint16 SubstituteNameLength uint16 PrintNameOffset uint16 PrintNameLength uint16 PathBuffer [1]uint16 } type symbolicLinkReparseBuffer struct { SubstituteNameOffset uint16 SubstituteNameLength uint16 PrintNameOffset uint16 PrintNameLength uint16 Flags uint32 PathBuffer [1]uint16 } const ( _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 _SYMLINK_FLAG_RELATIVE = 1 ) // Readlink returns the destination of the named symbolic link. func Readlink(path string, buf []byte) (n int, err error) { fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) if err != nil { return -1, err } defer syscall.CloseHandle(fd) rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) var bytesReturned uint32 err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) if err != nil { return -1, err } rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0])) var s string switch rdb.ReparseTag { case syscall.IO_REPARSE_TAG_SYMLINK: data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 { if len(s) >= 4 && s[:4] == `\??\` { s = s[4:] switch { case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar // do nothing case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar s = `\\` + s[4:] default: // unexpected; do nothing } } else { // unexpected; do nothing } } case _IO_REPARSE_TAG_MOUNT_POINT: data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar if len(s) < 48 || s[:11] != `\??\Volume{` { s = s[4:] } } else { // unexpected; do nothing } default: // the path is not a symlink or junction but another type of reparse // point return -1, syscall.ENOENT } n = copy(buf, []byte(s)) return n, nil }