forked from hashicorp/go-getter
-
Notifications
You must be signed in to change notification settings - Fork 3
/
get_file_windows.go
124 lines (105 loc) · 2.72 KB
/
get_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
// +build windows
package getter
import (
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)
func (g *FileGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
// The source path must exist and be a directory to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
} else if !fi.IsDir() {
return fmt.Errorf("source path must be a directory")
}
fi, err := os.Lstat(dst)
if err != nil && !os.IsNotExist(err) {
return err
}
// If the destination already exists, it must be a symlink
if err == nil {
mode := fi.Mode()
if mode&os.ModeSymlink == 0 {
return fmt.Errorf("destination exists and is not a symlink")
}
// Remove the destination
if err := os.Remove(dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), g.client.mode(0755)); err != nil {
return err
}
sourcePath := toBackslash(path)
// Use mklink to create a junction point
output, err := exec.CommandContext(ctx, "cmd", "/c", "mklink", "/J", dst, sourcePath).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to run mklink %v %v: %v %q", dst, sourcePath, err, output)
}
return nil
}
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
ctx := g.Context()
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
// The source path must exist and be a directory to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
} else if fi.IsDir() {
return fmt.Errorf("source path must be a file")
}
_, err := os.Lstat(dst)
if err != nil && !os.IsNotExist(err) {
return err
}
// If the destination already exists, it must be a symlink
if err == nil {
// Remove the destination
if err := os.Remove(dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), g.client.mode(0755)); err != nil {
return err
}
// If we're not copying, just symlink and we're done
if !g.Copy {
if err = os.Symlink(path, dst); err == nil {
return err
}
lerr, ok := err.(*os.LinkError)
if !ok {
return err
}
switch lerr.Err {
case syscall.ERROR_PRIVILEGE_NOT_HELD:
// no symlink privilege, let's
// fallback to a copy to avoid an error.
break
default:
return err
}
}
// Copy
_, err = copyFile(ctx, dst, path, 0666, g.client.umask())
return err
}
// toBackslash returns the result of replacing each slash character
// in path with a backslash ('\') character. Multiple separators are
// replaced by multiple backslashes.
func toBackslash(path string) string {
return strings.Replace(path, "/", "\\", -1)
}