aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/otiai10/copy/copy.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/otiai10/copy/copy.go')
-rw-r--r--vendor/github.com/otiai10/copy/copy.go284
1 files changed, 284 insertions, 0 deletions
diff --git a/vendor/github.com/otiai10/copy/copy.go b/vendor/github.com/otiai10/copy/copy.go
new file mode 100644
index 0000000..085db78
--- /dev/null
+++ b/vendor/github.com/otiai10/copy/copy.go
@@ -0,0 +1,284 @@
1package copy
2
3import (
4 "io"
5 "io/fs"
6 "io/ioutil"
7 "os"
8 "path/filepath"
9 "time"
10)
11
12type timespec struct {
13 Mtime time.Time
14 Atime time.Time
15 Ctime time.Time
16}
17
18// Copy copies src to dest, doesn't matter if src is a directory or a file.
19func Copy(src, dest string, opts ...Options) error {
20 opt := assureOptions(src, dest, opts...)
21 if opt.FS != nil {
22 info, err := fs.Stat(opt.FS, src)
23 if err != nil {
24 return onError(src, dest, err, opt)
25 }
26 return switchboard(src, dest, info, opt)
27 }
28 info, err := os.Lstat(src)
29 if err != nil {
30 return onError(src, dest, err, opt)
31 }
32 return switchboard(src, dest, info, opt)
33}
34
35// switchboard switches proper copy functions regarding file type, etc...
36// If there would be anything else here, add a case to this switchboard.
37func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) {
38 if info.Mode()&os.ModeDevice != 0 && !opt.Specials {
39 return onError(src, dest, err, opt)
40 }
41
42 switch {
43 case info.Mode()&os.ModeSymlink != 0:
44 err = onsymlink(src, dest, opt)
45 case info.IsDir():
46 err = dcopy(src, dest, info, opt)
47 case info.Mode()&os.ModeNamedPipe != 0:
48 err = pcopy(dest, info)
49 default:
50 err = fcopy(src, dest, info, opt)
51 }
52
53 return onError(src, dest, err, opt)
54}
55
56// copyNextOrSkip decide if this src should be copied or not.
57// Because this "copy" could be called recursively,
58// "info" MUST be given here, NOT nil.
59func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
60 if opt.Skip != nil {
61 skip, err := opt.Skip(info, src, dest)
62 if err != nil {
63 return err
64 }
65 if skip {
66 return nil
67 }
68 }
69 return switchboard(src, dest, info, opt)
70}
71
72// fcopy is for just a file,
73// with considering existence of parent directory
74// and file permission.
75func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
76
77 var readcloser io.ReadCloser
78 if opt.FS != nil {
79 readcloser, err = opt.FS.Open(src)
80 } else {
81 readcloser, err = os.Open(src)
82 }
83 if err != nil {
84 if os.IsNotExist(err) {
85 return nil
86 }
87 return
88 }
89 defer fclose(readcloser, &err)
90
91 if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
92 return
93 }
94
95 f, err := os.Create(dest)
96 if err != nil {
97 return
98 }
99 defer fclose(f, &err)
100
101 chmodfunc, err := opt.PermissionControl(info, dest)
102 if err != nil {
103 return err
104 }
105 chmodfunc(&err)
106
107 var buf []byte = nil
108 var w io.Writer = f
109 var r io.Reader = readcloser
110
111 if opt.WrapReader != nil {
112 r = opt.WrapReader(r)
113 }
114
115 if opt.CopyBufferSize != 0 {
116 buf = make([]byte, opt.CopyBufferSize)
117 // Disable using `ReadFrom` by io.CopyBuffer.
118 // See https://github.com/otiai10/copy/pull/60#discussion_r627320811 for more details.
119 w = struct{ io.Writer }{f}
120 // r = struct{ io.Reader }{s}
121 }
122
123 if _, err = io.CopyBuffer(w, r, buf); err != nil {
124 return err
125 }
126
127 if opt.Sync {
128 err = f.Sync()
129 }
130
131 if opt.PreserveOwner {
132 if err := preserveOwner(src, dest, info); err != nil {
133 return err
134 }
135 }
136 if opt.PreserveTimes {
137 if err := preserveTimes(info, dest); err != nil {
138 return err
139 }
140 }
141
142 return
143}
144
145// dcopy is for a directory,
146// with scanning contents inside the directory
147// and pass everything to "copy" recursively.
148func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
149 if skip, err := onDirExists(opt, srcdir, destdir); err != nil {
150 return err
151 } else if skip {
152 return nil
153 }
154
155 // Make dest dir with 0755 so that everything writable.
156 chmodfunc, err := opt.PermissionControl(info, destdir)
157 if err != nil {
158 return err
159 }
160 defer chmodfunc(&err)
161
162 var contents []os.FileInfo
163 if opt.FS != nil {
164 entries, err := fs.ReadDir(opt.FS, srcdir)
165 if err != nil {
166 return err
167 }
168 for _, e := range entries {
169 info, err := e.Info()
170 if err != nil {
171 return err
172 }
173 contents = append(contents, info)
174 }
175 } else {
176 contents, err = ioutil.ReadDir(srcdir)
177 }
178
179 if err != nil {
180 if os.IsNotExist(err) {
181 return nil
182 }
183 return
184 }
185
186 for _, content := range contents {
187 cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
188
189 if err = copyNextOrSkip(cs, cd, content, opt); err != nil {
190 // If any error, exit immediately
191 return
192 }
193 }
194
195 if opt.PreserveTimes {
196 if err := preserveTimes(info, destdir); err != nil {
197 return err
198 }
199 }
200
201 if opt.PreserveOwner {
202 if err := preserveOwner(srcdir, destdir, info); err != nil {
203 return err
204 }
205 }
206
207 return
208}
209
210func onDirExists(opt Options, srcdir, destdir string) (bool, error) {
211 _, err := os.Stat(destdir)
212 if err == nil && opt.OnDirExists != nil && destdir != opt.intent.dest {
213 switch opt.OnDirExists(srcdir, destdir) {
214 case Replace:
215 if err := os.RemoveAll(destdir); err != nil {
216 return false, err
217 }
218 case Untouchable:
219 return true, nil
220 } // case "Merge" is default behaviour. Go through.
221 } else if err != nil && !os.IsNotExist(err) {
222 return true, err // Unwelcome error type...!
223 }
224 return false, nil
225}
226
227func onsymlink(src, dest string, opt Options) error {
228 switch opt.OnSymlink(src) {
229 case Shallow:
230 if err := lcopy(src, dest); err != nil {
231 return err
232 }
233 if opt.PreserveTimes {
234 return preserveLtimes(src, dest)
235 }
236 return nil
237 case Deep:
238 orig, err := os.Readlink(src)
239 if err != nil {
240 return err
241 }
242 info, err := os.Lstat(orig)
243 if err != nil {
244 return err
245 }
246 return copyNextOrSkip(orig, dest, info, opt)
247 case Skip:
248 fallthrough
249 default:
250 return nil // do nothing
251 }
252}
253
254// lcopy is for a symlink,
255// with just creating a new symlink by replicating src symlink.
256func lcopy(src, dest string) error {
257 src, err := os.Readlink(src)
258 if err != nil {
259 if os.IsNotExist(err) {
260 return nil
261 }
262 return err
263 }
264 return os.Symlink(src, dest)
265}
266
267// fclose ANYHOW closes file,
268// with asiging error raised during Close,
269// BUT respecting the error already reported.
270func fclose(f io.Closer, reported *error) {
271 if err := f.Close(); *reported == nil {
272 *reported = err
273 }
274}
275
276// onError lets caller to handle errors
277// occured when copying a file.
278func onError(src, dest string, err error, opt Options) error {
279 if opt.OnError == nil {
280 return err
281 }
282
283 return opt.OnError(src, dest, err)
284}