blob: 9bb3936cd177dc7a1799a1de32b55b680d0b3d90 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2013 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4// +build windows
5
6package utils
7
8import (
9 "fmt"
10 "os"
11 "path/filepath"
12 "syscall"
13 "unsafe"
14
15 "github.com/juju/errors"
16)
17
18const (
19 movefile_replace_existing = 0x1
20 movefile_write_through = 0x8
21)
22
23//sys moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFlags uint32) (err error) = MoveFileExW
24
25// MoveFile atomically moves the source file to the destination, returning
26// whether the file was moved successfully. If the destination already exists,
27// it returns an error rather than overwrite it.
28func MoveFile(source, destination string) (bool, error) {
29 src, err := syscall.UTF16PtrFromString(source)
30 if err != nil {
31 return false, &os.LinkError{"move", source, destination, err}
32 }
33 dest, err := syscall.UTF16PtrFromString(destination)
34 if err != nil {
35 return false, &os.LinkError{"move", source, destination, err}
36 }
37
38 // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
39 if err := moveFileEx(src, dest, movefile_write_through); err != nil {
40 return false, &os.LinkError{"move", source, destination, err}
41 }
42 return true, nil
43
44}
45
46// ReplaceFile atomically replaces the destination file or directory with the source.
47// The errors that are returned are identical to those returned by os.Rename.
48func ReplaceFile(source, destination string) error {
49 src, err := syscall.UTF16PtrFromString(source)
50 if err != nil {
51 return &os.LinkError{"replace", source, destination, err}
52 }
53 dest, err := syscall.UTF16PtrFromString(destination)
54 if err != nil {
55 return &os.LinkError{"replace", source, destination, err}
56 }
57
58 // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
59 if err := moveFileEx(src, dest, movefile_replace_existing|movefile_write_through); err != nil {
60 return &os.LinkError{"replace", source, destination, err}
61 }
62 return nil
63}
64
65// MakeFileURL returns a proper file URL for the given path/directory
66func MakeFileURL(in string) string {
67 in = filepath.ToSlash(in)
68 // for windows at least should be <letter>: to be considered valid
69 // so we cant do anything with less than that.
70 if len(in) < 2 {
71 return in
72 }
73 if string(in[1]) != ":" {
74 return in
75 }
76 // since go 1.6 http client will only take this format.
77 return "file://" + in
78}
79
80func getUserSID(username string) (string, error) {
81 sid, _, _, e := syscall.LookupSID("", username)
82 if e != nil {
83 return "", e
84 }
85 sidStr, err := sid.String()
86 return sidStr, err
87}
88
89func readRegString(h syscall.Handle, key string) (value string, err error) {
90 var typ uint32
91 var buf uint32
92
93 // Get size of registry key
94 err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, nil, &buf)
95 if err != nil {
96 return value, err
97 }
98
99 n := make([]uint16, buf/2+1)
100 err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(key), nil, &typ, (*byte)(unsafe.Pointer(&n[0])), &buf)
101 if err != nil {
102 return value, err
103 }
104 return syscall.UTF16ToString(n[:]), err
105}
106
107func homeFromRegistry(sid string) (string, error) {
108 var h syscall.Handle
109 // This key will exist on all platforms we support the agent on (windows server 2008 and above)
110 keyPath := fmt.Sprintf("Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\%s", sid)
111 err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
112 syscall.StringToUTF16Ptr(keyPath),
113 0, syscall.KEY_READ, &h)
114 if err != nil {
115 return "", err
116 }
117 defer syscall.RegCloseKey(h)
118 str, err := readRegString(h, "ProfileImagePath")
119 if err != nil {
120 return "", err
121 }
122 return str, nil
123}
124
125// homeDir returns a local user home dir on Windows
126// user.Lookup() does not populate Gid and HomeDir on Windows,
127// so we get it from the registry
128func homeDir(user string) (string, error) {
129 u, err := getUserSID(user)
130 if err != nil {
131 return "", errors.NewUserNotFound(err, "no such user")
132 }
133 return homeFromRegistry(u)
134}
135
136// ChownPath is not implemented for Windows.
137func ChownPath(path, username string) error {
138 // This only exists to allow building on Windows. User lookup and
139 // file ownership needs to be handled in a completely different
140 // way and hasn't yet been implemented.
141 return nil
142}