1// Licensed to Elasticsearch B.V. under one or more contributor 2// license agreements. See the NOTICE file distributed with 3// this work for additional information regarding copyright 4// ownership. Elasticsearch B.V. licenses this file to you under 5// the Apache License, Version 2.0 (the "License"); you may 6// not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, 12// software distributed under the License is distributed on an 13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14// KIND, either express or implied. See the License for the 15// specific language governing permissions and limitations 16// under the License. 17 18// +build windows 19 20package windows 21 22import ( 23 "fmt" 24 "unsafe" 25 26 "github.com/pkg/errors" 27) 28 29// Syscalls 30//sys _GetFileVersionInfo(filename string, reserved uint32, dataLen uint32, data *byte) (success bool, err error) [!success] = version.GetFileVersionInfoW 31//sys _GetFileVersionInfoSize(filename string, handle uintptr) (size uint32, err error) = version.GetFileVersionInfoSizeW 32//sys _VerQueryValueW(data *byte, subBlock string, pBuffer *uintptr, len *uint32) (success bool, err error) [!success] = version.VerQueryValueW 33 34// FixedFileInfo contains version information for a file. This information is 35// language and code page independent. This is an equivalent representation of 36// VS_FIXEDFILEINFO. 37// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx 38type FixedFileInfo struct { 39 Signature uint32 40 StrucVersion uint32 41 FileVersionMS uint32 42 FileVersionLS uint32 43 ProductVersionMS uint32 44 ProductVersionLS uint32 45 FileFlagsMask uint32 46 FileFlags uint32 47 FileOS uint32 48 FileType uint32 49 FileSubtype uint32 50 FileDateMS uint32 51 FileDateLS uint32 52} 53 54// ProductVersion returns the ProductVersion value in string format. 55func (info FixedFileInfo) ProductVersion() string { 56 return fmt.Sprintf("%d.%d.%d.%d", 57 (info.ProductVersionMS >> 16), 58 (info.ProductVersionMS & 0xFFFF), 59 (info.ProductVersionLS >> 16), 60 (info.ProductVersionLS & 0xFFFF)) 61} 62 63// FileVersion returns the FileVersion value in string format. 64func (info FixedFileInfo) FileVersion() string { 65 return fmt.Sprintf("%d.%d.%d.%d", 66 (info.FileVersionMS >> 16), 67 (info.FileVersionMS & 0xFFFF), 68 (info.FileVersionLS >> 16), 69 (info.FileVersionLS & 0xFFFF)) 70} 71 72// VersionData is a buffer holding the data returned by GetFileVersionInfo. 73type VersionData []byte 74 75// QueryValue uses VerQueryValue to query version information from the a 76// version-information resource. It returns responses using the first language 77// and code point found in the resource. The accepted keys are listed in 78// the VerQueryValue documentation (e.g. ProductVersion, FileVersion, etc.). 79// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx 80func (d VersionData) QueryValue(key string) (string, error) { 81 type LangAndCodePage struct { 82 Language uint16 83 CodePage uint16 84 } 85 86 var dataPtr uintptr 87 var size uint32 88 if _, err := _VerQueryValueW(&d[0], `\VarFileInfo\Translation`, &dataPtr, &size); err != nil || size == 0 { 89 return "", errors.Wrap(err, "failed to get list of languages") 90 } 91 92 offset := int(dataPtr - (uintptr)(unsafe.Pointer(&d[0]))) 93 if offset <= 0 || offset > len(d)-1 { 94 return "", errors.New("invalid address") 95 } 96 97 l := *(*LangAndCodePage)(unsafe.Pointer(&d[offset])) 98 99 subBlock := fmt.Sprintf(`\StringFileInfo\%04x%04x\%v`, l.Language, l.CodePage, key) 100 if _, err := _VerQueryValueW(&d[0], subBlock, &dataPtr, &size); err != nil || size == 0 { 101 return "", errors.Wrapf(err, "failed to query %v", subBlock) 102 } 103 104 offset = int(dataPtr - (uintptr)(unsafe.Pointer(&d[0]))) 105 if offset <= 0 || offset > len(d)-1 { 106 return "", errors.New("invalid address") 107 } 108 109 str, _, err := UTF16BytesToString(d[offset : offset+int(size)*2]) 110 if err != nil { 111 return "", errors.Wrap(err, "failed to decode UTF16 data") 112 } 113 114 return str, nil 115} 116 117// FixedFileInfo returns the fixed version information from a 118// version-information resource. It queries the root block to get the 119// VS_FIXEDFILEINFO value. 120// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx 121func (d VersionData) FixedFileInfo() (*FixedFileInfo, error) { 122 if len(d) == 0 { 123 return nil, errors.New("use GetFileVersionInfo to initialize VersionData") 124 } 125 126 var dataPtr uintptr 127 var size uint32 128 if _, err := _VerQueryValueW(&d[0], `\`, &dataPtr, &size); err != nil { 129 return nil, errors.Wrap(err, "VerQueryValue failed for \\") 130 } 131 132 offset := int(dataPtr - (uintptr)(unsafe.Pointer(&d[0]))) 133 if offset <= 0 || offset > len(d)-1 { 134 return nil, errors.New("invalid address") 135 } 136 137 // Make a copy of the struct. 138 ffi := *(*FixedFileInfo)(unsafe.Pointer(&d[offset])) 139 140 return &ffi, nil 141} 142 143// GetFileVersionInfo retrieves version information for the specified file. 144// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647003(v=vs.85).aspx 145func GetFileVersionInfo(filename string) (VersionData, error) { 146 size, err := _GetFileVersionInfoSize(filename, 0) 147 if err != nil { 148 return nil, errors.Wrap(err, "GetFileVersionInfoSize failed") 149 } 150 151 data := make(VersionData, size) 152 _, err = _GetFileVersionInfo(filename, 0, uint32(len(data)), &data[0]) 153 if err != nil { 154 return nil, errors.Wrap(err, "GetFileVersionInfo failed") 155 } 156 157 return data, nil 158} 159