-
Notifications
You must be signed in to change notification settings - Fork 89
/
file_windows.go
148 lines (128 loc) · 4.23 KB
/
file_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
//go:build windows
// +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
}
// IsFileOwner is not implemented for Windows.
func IsFileOwner(path, username string) (bool, error) {
return true, nil
}