forked from kubernetes-sigs/kind
/
fs.go
144 lines (132 loc) · 3.5 KB
/
fs.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
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package fs contains utilites for interacting with the host filesystem
// in a docker friendly way
package fs
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
)
// TempDir is like ioutil.TempDir, but more docker friendly
func TempDir(dir, prefix string) (name string, err error) {
// create a tempdir as normal
name, err = ioutil.TempDir(dir, prefix)
if err != nil {
return "", err
}
// on macOS $TMPDIR is typically /var/..., which is not mountable
// /private/var/... is the mountable equivalent
if runtime.GOOS == "darwin" && strings.HasPrefix(name, "/var/") {
name = filepath.Join("/private", name)
}
return name, nil
}
// Copy recursively directories, symlinks, files copies from src to dst
// Copy will make dirs as necessary, and keep file modes
// Symlinks will be dereferenced similar to `cp -r src dst`
func Copy(src, dst string) error {
// get source info
info, err := os.Lstat(src)
if err != nil {
return err
}
// make sure dest dir exists
if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
return err
}
// do real copy work
return copy(src, dst, info)
}
func copy(src, dst string, info os.FileInfo) error {
if info.Mode()&os.ModeSymlink != 0 {
return copySymlink(src, dst)
}
if info.IsDir() {
return copyDir(src, dst, info)
}
return copyFile(src, dst, info)
}
// CopyFile copies a file from src to dst
func CopyFile(src, dst string) (err error) {
// get source information
info, err := os.Stat(src)
if err != nil {
return err
}
return copyFile(src, dst, info)
}
func copyFile(src, dst string, info os.FileInfo) error {
// open src for reading
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
// create dst file
// this is like f, err := os.Create(dst); os.Chmod(f.Name(), src.Mode())
out, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
if err != nil {
return err
}
// make sure we close the file
defer func() {
closeErr := out.Close()
// if we weren't returning an error
if err == nil {
err = closeErr
}
}()
// actually copy
if _, err = io.Copy(out, in); err != nil {
return err
}
err = out.Sync()
return err
}
// copySymlink dereferences and then copies a symlink
func copySymlink(src, dst string) error {
// read through the symlink
realSrc, err := filepath.EvalSymlinks(src)
if err != nil {
return err
}
info, err := os.Lstat(realSrc)
if err != nil {
return err
}
// copy the underlying contents
return copy(realSrc, dst, info)
}
func copyDir(src, dst string, info os.FileInfo) error {
// make sure the target dir exists
if err := os.MkdirAll(dst, info.Mode()); err != nil {
return err
}
// copy every source dir entry
entries, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, entry := range entries {
entrySrc := filepath.Join(src, entry.Name())
entryDst := filepath.Join(dst, entry.Name())
if err := copy(entrySrc, entryDst, entry); err != nil {
return err
}
}
return nil
}