blob: 9bb3936cd177dc7a1799a1de32b55b680d0b3d90 [file] [log] [blame]
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// +build windows
package utils
import (
"fmt"
"os"
"path/filepath"
"syscall"
"unsafe"
"github.com/juju/errors"
)
const (
movefile_replace_existing = 0x1
movefile_write_through = 0x8
)
//sys moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFlags uint32) (err error) = MoveFileExW
// MoveFile atomically moves the source file to the destination, returning
// whether the file was moved successfully. If the destination already exists,
// it returns an error rather than overwrite it.
func MoveFile(source, destination string) (bool, error) {
src, err := syscall.UTF16PtrFromString(source)
if err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
dest, err := syscall.UTF16PtrFromString(destination)
if err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
// see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
if err := moveFileEx(src, dest, movefile_write_through); err != nil {
return false, &os.LinkError{"move", source, destination, err}
}
return true, nil
}
// ReplaceFile atomically replaces the destination file or directory with the source.
// The errors that are returned are identical to those returned by os.Rename.
func ReplaceFile(source, destination string) error {
src, err := syscall.UTF16PtrFromString(source)
if err != nil {
return &os.LinkError{"replace", source, destination, err}
}
dest, err := syscall.UTF16PtrFromString(destination)
if err != nil {
return &os.LinkError{"replace", source, destination, err}
}
// see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
if err := moveFileEx(src, dest, movefile_replace_existing|movefile_write_through); err != nil {
return &os.LinkError{"replace", source, destination, err}
}
return nil
}
// MakeFileURL returns a proper file URL for the given path/directory
func MakeFileURL(in string) string {
in = filepath.ToSlash(in)
// for windows at least should be <letter>: to be considered valid
// so we cant do anything with less than that.
if len(in) < 2 {
return in
}
if string(in[1]) != ":" {
return in
}
// since go 1.6 http client will only take this format.
return "file://" + in
}
func getUserSID(username string) (string, error) {
sid, _, _, e := syscall.LookupSID("", username)
if e != nil {
return "", e
}
sidStr, err := sid.String()
return sidStr, err
}
func readRegString(h syscall.Handle, key string) (value string, err error) {
var typ uint32
var buf uint32
// Get size of registry key
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, nil, &buf)
if err != nil {
return value, err
}
n := make([]uint16, buf/2+1)
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, (*byte)(unsafe.Pointer(&n[0])), &buf)
if err != nil {
return value, err
}
return syscall.UTF16ToString(n[:]), err
}
func homeFromRegistry(sid string) (string, error) {
var h syscall.Handle
// This key will exist on all platforms we support the agent on (windows server 2008 and above)
keyPath := fmt.Sprintf("Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s", sid)
err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
syscall.StringToUTF16Ptr(keyPath),
0, syscall.KEY_READ, &h)
if err != nil {
return "", err
}
defer syscall.RegCloseKey(h)
str, err := readRegString(h, "ProfileImagePath")
if err != nil {
return "", err
}
return str, nil
}
// homeDir returns a local user home dir on Windows
// user.Lookup() does not populate Gid and HomeDir on Windows,
// so we get it from the registry
func homeDir(user string) (string, error) {
u, err := getUserSID(user)
if err != nil {
return "", errors.NewUserNotFound(err, "no such user")
}
return homeFromRegistry(u)
}
// ChownPath is not implemented for Windows.
func ChownPath(path, username string) error {
// This only exists to allow building on Windows. User lookup and
// file ownership needs to be handled in a completely different
// way and hasn't yet been implemented.
return nil
}