diff options
Diffstat (limited to 'vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy.go')
-rw-r--r-- | vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy.go | 458 |
1 files changed, 0 insertions, 458 deletions
diff --git a/vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy.go b/vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy.go deleted file mode 100644 index e950912..0000000 --- a/vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy.go +++ /dev/null | |||
@@ -1,458 +0,0 @@ | |||
1 | package archive | ||
2 | |||
3 | import ( | ||
4 | "archive/tar" | ||
5 | "errors" | ||
6 | "io" | ||
7 | "io/ioutil" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | "strings" | ||
11 | |||
12 | "github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus" | ||
13 | "github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system" | ||
14 | ) | ||
15 | |||
16 | // Errors used or returned by this file. | ||
17 | var ( | ||
18 | ErrNotDirectory = errors.New("not a directory") | ||
19 | ErrDirNotExists = errors.New("no such directory") | ||
20 | ErrCannotCopyDir = errors.New("cannot copy directory") | ||
21 | ErrInvalidCopySource = errors.New("invalid copy source content") | ||
22 | ) | ||
23 | |||
24 | // PreserveTrailingDotOrSeparator returns the given cleaned path (after | ||
25 | // processing using any utility functions from the path or filepath stdlib | ||
26 | // packages) and appends a trailing `/.` or `/` if its corresponding original | ||
27 | // path (from before being processed by utility functions from the path or | ||
28 | // filepath stdlib packages) ends with a trailing `/.` or `/`. If the cleaned | ||
29 | // path already ends in a `.` path segment, then another is not added. If the | ||
30 | // clean path already ends in a path separator, then another is not added. | ||
31 | func PreserveTrailingDotOrSeparator(cleanedPath, originalPath string) string { | ||
32 | // Ensure paths are in platform semantics | ||
33 | cleanedPath = normalizePath(cleanedPath) | ||
34 | originalPath = normalizePath(originalPath) | ||
35 | |||
36 | if !specifiesCurrentDir(cleanedPath) && specifiesCurrentDir(originalPath) { | ||
37 | if !hasTrailingPathSeparator(cleanedPath) { | ||
38 | // Add a separator if it doesn't already end with one (a cleaned | ||
39 | // path would only end in a separator if it is the root). | ||
40 | cleanedPath += string(filepath.Separator) | ||
41 | } | ||
42 | cleanedPath += "." | ||
43 | } | ||
44 | |||
45 | if !hasTrailingPathSeparator(cleanedPath) && hasTrailingPathSeparator(originalPath) { | ||
46 | cleanedPath += string(filepath.Separator) | ||
47 | } | ||
48 | |||
49 | return cleanedPath | ||
50 | } | ||
51 | |||
52 | // assertsDirectory returns whether the given path is | ||
53 | // asserted to be a directory, i.e., the path ends with | ||
54 | // a trailing '/' or `/.`, assuming a path separator of `/`. | ||
55 | func assertsDirectory(path string) bool { | ||
56 | return hasTrailingPathSeparator(path) || specifiesCurrentDir(path) | ||
57 | } | ||
58 | |||
59 | // hasTrailingPathSeparator returns whether the given | ||
60 | // path ends with the system's path separator character. | ||
61 | func hasTrailingPathSeparator(path string) bool { | ||
62 | return len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) | ||
63 | } | ||
64 | |||
65 | // specifiesCurrentDir returns whether the given path specifies | ||
66 | // a "current directory", i.e., the last path segment is `.`. | ||
67 | func specifiesCurrentDir(path string) bool { | ||
68 | return filepath.Base(path) == "." | ||
69 | } | ||
70 | |||
71 | // SplitPathDirEntry splits the given path between its directory name and its | ||
72 | // basename by first cleaning the path but preserves a trailing "." if the | ||
73 | // original path specified the current directory. | ||
74 | func SplitPathDirEntry(path string) (dir, base string) { | ||
75 | cleanedPath := filepath.Clean(normalizePath(path)) | ||
76 | |||
77 | if specifiesCurrentDir(path) { | ||
78 | cleanedPath += string(filepath.Separator) + "." | ||
79 | } | ||
80 | |||
81 | return filepath.Dir(cleanedPath), filepath.Base(cleanedPath) | ||
82 | } | ||
83 | |||
84 | // TarResource archives the resource described by the given CopyInfo to a Tar | ||
85 | // archive. A non-nil error is returned if sourcePath does not exist or is | ||
86 | // asserted to be a directory but exists as another type of file. | ||
87 | // | ||
88 | // This function acts as a convenient wrapper around TarWithOptions, which | ||
89 | // requires a directory as the source path. TarResource accepts either a | ||
90 | // directory or a file path and correctly sets the Tar options. | ||
91 | func TarResource(sourceInfo CopyInfo) (content Archive, err error) { | ||
92 | return TarResourceRebase(sourceInfo.Path, sourceInfo.RebaseName) | ||
93 | } | ||
94 | |||
95 | // TarResourceRebase is like TarResource but renames the first path element of | ||
96 | // items in the resulting tar archive to match the given rebaseName if not "". | ||
97 | func TarResourceRebase(sourcePath, rebaseName string) (content Archive, err error) { | ||
98 | sourcePath = normalizePath(sourcePath) | ||
99 | if _, err = os.Lstat(sourcePath); err != nil { | ||
100 | // Catches the case where the source does not exist or is not a | ||
101 | // directory if asserted to be a directory, as this also causes an | ||
102 | // error. | ||
103 | return | ||
104 | } | ||
105 | |||
106 | // Separate the source path between it's directory and | ||
107 | // the entry in that directory which we are archiving. | ||
108 | sourceDir, sourceBase := SplitPathDirEntry(sourcePath) | ||
109 | |||
110 | filter := []string{sourceBase} | ||
111 | |||
112 | logrus.Debugf("copying %q from %q", sourceBase, sourceDir) | ||
113 | |||
114 | return TarWithOptions(sourceDir, &TarOptions{ | ||
115 | Compression: Uncompressed, | ||
116 | IncludeFiles: filter, | ||
117 | IncludeSourceDir: true, | ||
118 | RebaseNames: map[string]string{ | ||
119 | sourceBase: rebaseName, | ||
120 | }, | ||
121 | }) | ||
122 | } | ||
123 | |||
124 | // CopyInfo holds basic info about the source | ||
125 | // or destination path of a copy operation. | ||
126 | type CopyInfo struct { | ||
127 | Path string | ||
128 | Exists bool | ||
129 | IsDir bool | ||
130 | RebaseName string | ||
131 | } | ||
132 | |||
133 | // CopyInfoSourcePath stats the given path to create a CopyInfo | ||
134 | // struct representing that resource for the source of an archive copy | ||
135 | // operation. The given path should be an absolute local path. A source path | ||
136 | // has all symlinks evaluated that appear before the last path separator ("/" | ||
137 | // on Unix). As it is to be a copy source, the path must exist. | ||
138 | func CopyInfoSourcePath(path string, followLink bool) (CopyInfo, error) { | ||
139 | // normalize the file path and then evaluate the symbol link | ||
140 | // we will use the target file instead of the symbol link if | ||
141 | // followLink is set | ||
142 | path = normalizePath(path) | ||
143 | |||
144 | resolvedPath, rebaseName, err := ResolveHostSourcePath(path, followLink) | ||
145 | if err != nil { | ||
146 | return CopyInfo{}, err | ||
147 | } | ||
148 | |||
149 | stat, err := os.Lstat(resolvedPath) | ||
150 | if err != nil { | ||
151 | return CopyInfo{}, err | ||
152 | } | ||
153 | |||
154 | return CopyInfo{ | ||
155 | Path: resolvedPath, | ||
156 | Exists: true, | ||
157 | IsDir: stat.IsDir(), | ||
158 | RebaseName: rebaseName, | ||
159 | }, nil | ||
160 | } | ||
161 | |||
162 | // CopyInfoDestinationPath stats the given path to create a CopyInfo | ||
163 | // struct representing that resource for the destination of an archive copy | ||
164 | // operation. The given path should be an absolute local path. | ||
165 | func CopyInfoDestinationPath(path string) (info CopyInfo, err error) { | ||
166 | maxSymlinkIter := 10 // filepath.EvalSymlinks uses 255, but 10 already seems like a lot. | ||
167 | path = normalizePath(path) | ||
168 | originalPath := path | ||
169 | |||
170 | stat, err := os.Lstat(path) | ||
171 | |||
172 | if err == nil && stat.Mode()&os.ModeSymlink == 0 { | ||
173 | // The path exists and is not a symlink. | ||
174 | return CopyInfo{ | ||
175 | Path: path, | ||
176 | Exists: true, | ||
177 | IsDir: stat.IsDir(), | ||
178 | }, nil | ||
179 | } | ||
180 | |||
181 | // While the path is a symlink. | ||
182 | for n := 0; err == nil && stat.Mode()&os.ModeSymlink != 0; n++ { | ||
183 | if n > maxSymlinkIter { | ||
184 | // Don't follow symlinks more than this arbitrary number of times. | ||
185 | return CopyInfo{}, errors.New("too many symlinks in " + originalPath) | ||
186 | } | ||
187 | |||
188 | // The path is a symbolic link. We need to evaluate it so that the | ||
189 | // destination of the copy operation is the link target and not the | ||
190 | // link itself. This is notably different than CopyInfoSourcePath which | ||
191 | // only evaluates symlinks before the last appearing path separator. | ||
192 | // Also note that it is okay if the last path element is a broken | ||
193 | // symlink as the copy operation should create the target. | ||
194 | var linkTarget string | ||
195 | |||
196 | linkTarget, err = os.Readlink(path) | ||
197 | if err != nil { | ||
198 | return CopyInfo{}, err | ||
199 | } | ||
200 | |||
201 | if !system.IsAbs(linkTarget) { | ||
202 | // Join with the parent directory. | ||
203 | dstParent, _ := SplitPathDirEntry(path) | ||
204 | linkTarget = filepath.Join(dstParent, linkTarget) | ||
205 | } | ||
206 | |||
207 | path = linkTarget | ||
208 | stat, err = os.Lstat(path) | ||
209 | } | ||
210 | |||
211 | if err != nil { | ||
212 | // It's okay if the destination path doesn't exist. We can still | ||
213 | // continue the copy operation if the parent directory exists. | ||
214 | if !os.IsNotExist(err) { | ||
215 | return CopyInfo{}, err | ||
216 | } | ||
217 | |||
218 | // Ensure destination parent dir exists. | ||
219 | dstParent, _ := SplitPathDirEntry(path) | ||
220 | |||
221 | parentDirStat, err := os.Lstat(dstParent) | ||
222 | if err != nil { | ||
223 | return CopyInfo{}, err | ||
224 | } | ||
225 | if !parentDirStat.IsDir() { | ||
226 | return CopyInfo{}, ErrNotDirectory | ||
227 | } | ||
228 | |||
229 | return CopyInfo{Path: path}, nil | ||
230 | } | ||
231 | |||
232 | // The path exists after resolving symlinks. | ||
233 | return CopyInfo{ | ||
234 | Path: path, | ||
235 | Exists: true, | ||
236 | IsDir: stat.IsDir(), | ||
237 | }, nil | ||
238 | } | ||
239 | |||
240 | // PrepareArchiveCopy prepares the given srcContent archive, which should | ||
241 | // contain the archived resource described by srcInfo, to the destination | ||
242 | // described by dstInfo. Returns the possibly modified content archive along | ||
243 | // with the path to the destination directory which it should be extracted to. | ||
244 | func PrepareArchiveCopy(srcContent Reader, srcInfo, dstInfo CopyInfo) (dstDir string, content Archive, err error) { | ||
245 | // Ensure in platform semantics | ||
246 | srcInfo.Path = normalizePath(srcInfo.Path) | ||
247 | dstInfo.Path = normalizePath(dstInfo.Path) | ||
248 | |||
249 | // Separate the destination path between its directory and base | ||
250 | // components in case the source archive contents need to be rebased. | ||
251 | dstDir, dstBase := SplitPathDirEntry(dstInfo.Path) | ||
252 | _, srcBase := SplitPathDirEntry(srcInfo.Path) | ||
253 | |||
254 | switch { | ||
255 | case dstInfo.Exists && dstInfo.IsDir: | ||
256 | // The destination exists as a directory. No alteration | ||
257 | // to srcContent is needed as its contents can be | ||
258 | // simply extracted to the destination directory. | ||
259 | return dstInfo.Path, ioutil.NopCloser(srcContent), nil | ||
260 | case dstInfo.Exists && srcInfo.IsDir: | ||
261 | // The destination exists as some type of file and the source | ||
262 | // content is a directory. This is an error condition since | ||
263 | // you cannot copy a directory to an existing file location. | ||
264 | return "", nil, ErrCannotCopyDir | ||
265 | case dstInfo.Exists: | ||
266 | // The destination exists as some type of file and the source content | ||
267 | // is also a file. The source content entry will have to be renamed to | ||
268 | // have a basename which matches the destination path's basename. | ||
269 | if len(srcInfo.RebaseName) != 0 { | ||
270 | srcBase = srcInfo.RebaseName | ||
271 | } | ||
272 | return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil | ||
273 | case srcInfo.IsDir: | ||
274 | // The destination does not exist and the source content is an archive | ||
275 | // of a directory. The archive should be extracted to the parent of | ||
276 | // the destination path instead, and when it is, the directory that is | ||
277 | // created as a result should take the name of the destination path. | ||
278 | // The source content entries will have to be renamed to have a | ||
279 | // basename which matches the destination path's basename. | ||
280 | if len(srcInfo.RebaseName) != 0 { | ||
281 | srcBase = srcInfo.RebaseName | ||
282 | } | ||
283 | return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil | ||
284 | case assertsDirectory(dstInfo.Path): | ||
285 | // The destination does not exist and is asserted to be created as a | ||
286 | // directory, but the source content is not a directory. This is an | ||
287 | // error condition since you cannot create a directory from a file | ||
288 | // source. | ||
289 | return "", nil, ErrDirNotExists | ||
290 | default: | ||
291 | // The last remaining case is when the destination does not exist, is | ||
292 | // not asserted to be a directory, and the source content is not an | ||
293 | // archive of a directory. It this case, the destination file will need | ||
294 | // to be created when the archive is extracted and the source content | ||
295 | // entry will have to be renamed to have a basename which matches the | ||
296 | // destination path's basename. | ||
297 | if len(srcInfo.RebaseName) != 0 { | ||
298 | srcBase = srcInfo.RebaseName | ||
299 | } | ||
300 | return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil | ||
301 | } | ||
302 | |||
303 | } | ||
304 | |||
305 | // RebaseArchiveEntries rewrites the given srcContent archive replacing | ||
306 | // an occurrence of oldBase with newBase at the beginning of entry names. | ||
307 | func RebaseArchiveEntries(srcContent Reader, oldBase, newBase string) Archive { | ||
308 | if oldBase == string(os.PathSeparator) { | ||
309 | // If oldBase specifies the root directory, use an empty string as | ||
310 | // oldBase instead so that newBase doesn't replace the path separator | ||
311 | // that all paths will start with. | ||
312 | oldBase = "" | ||
313 | } | ||
314 | |||
315 | rebased, w := io.Pipe() | ||
316 | |||
317 | go func() { | ||
318 | srcTar := tar.NewReader(srcContent) | ||
319 | rebasedTar := tar.NewWriter(w) | ||
320 | |||
321 | for { | ||
322 | hdr, err := srcTar.Next() | ||
323 | if err == io.EOF { | ||
324 | // Signals end of archive. | ||
325 | rebasedTar.Close() | ||
326 | w.Close() | ||
327 | return | ||
328 | } | ||
329 | if err != nil { | ||
330 | w.CloseWithError(err) | ||
331 | return | ||
332 | } | ||
333 | |||
334 | hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) | ||
335 | |||
336 | if err = rebasedTar.WriteHeader(hdr); err != nil { | ||
337 | w.CloseWithError(err) | ||
338 | return | ||
339 | } | ||
340 | |||
341 | if _, err = io.Copy(rebasedTar, srcTar); err != nil { | ||
342 | w.CloseWithError(err) | ||
343 | return | ||
344 | } | ||
345 | } | ||
346 | }() | ||
347 | |||
348 | return rebased | ||
349 | } | ||
350 | |||
351 | // CopyResource performs an archive copy from the given source path to the | ||
352 | // given destination path. The source path MUST exist and the destination | ||
353 | // path's parent directory must exist. | ||
354 | func CopyResource(srcPath, dstPath string, followLink bool) error { | ||
355 | var ( | ||
356 | srcInfo CopyInfo | ||
357 | err error | ||
358 | ) | ||
359 | |||
360 | // Ensure in platform semantics | ||
361 | srcPath = normalizePath(srcPath) | ||
362 | dstPath = normalizePath(dstPath) | ||
363 | |||
364 | // Clean the source and destination paths. | ||
365 | srcPath = PreserveTrailingDotOrSeparator(filepath.Clean(srcPath), srcPath) | ||
366 | dstPath = PreserveTrailingDotOrSeparator(filepath.Clean(dstPath), dstPath) | ||
367 | |||
368 | if srcInfo, err = CopyInfoSourcePath(srcPath, followLink); err != nil { | ||
369 | return err | ||
370 | } | ||
371 | |||
372 | content, err := TarResource(srcInfo) | ||
373 | if err != nil { | ||
374 | return err | ||
375 | } | ||
376 | defer content.Close() | ||
377 | |||
378 | return CopyTo(content, srcInfo, dstPath) | ||
379 | } | ||
380 | |||
381 | // CopyTo handles extracting the given content whose | ||
382 | // entries should be sourced from srcInfo to dstPath. | ||
383 | func CopyTo(content Reader, srcInfo CopyInfo, dstPath string) error { | ||
384 | // The destination path need not exist, but CopyInfoDestinationPath will | ||
385 | // ensure that at least the parent directory exists. | ||
386 | dstInfo, err := CopyInfoDestinationPath(normalizePath(dstPath)) | ||
387 | if err != nil { | ||
388 | return err | ||
389 | } | ||
390 | |||
391 | dstDir, copyArchive, err := PrepareArchiveCopy(content, srcInfo, dstInfo) | ||
392 | if err != nil { | ||
393 | return err | ||
394 | } | ||
395 | defer copyArchive.Close() | ||
396 | |||
397 | options := &TarOptions{ | ||
398 | NoLchown: true, | ||
399 | NoOverwriteDirNonDir: true, | ||
400 | } | ||
401 | |||
402 | return Untar(copyArchive, dstDir, options) | ||
403 | } | ||
404 | |||
405 | // ResolveHostSourcePath decides real path need to be copied with parameters such as | ||
406 | // whether to follow symbol link or not, if followLink is true, resolvedPath will return | ||
407 | // link target of any symbol link file, else it will only resolve symlink of directory | ||
408 | // but return symbol link file itself without resolving. | ||
409 | func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, err error) { | ||
410 | if followLink { | ||
411 | resolvedPath, err = filepath.EvalSymlinks(path) | ||
412 | if err != nil { | ||
413 | return | ||
414 | } | ||
415 | |||
416 | resolvedPath, rebaseName = GetRebaseName(path, resolvedPath) | ||
417 | } else { | ||
418 | dirPath, basePath := filepath.Split(path) | ||
419 | |||
420 | // if not follow symbol link, then resolve symbol link of parent dir | ||
421 | var resolvedDirPath string | ||
422 | resolvedDirPath, err = filepath.EvalSymlinks(dirPath) | ||
423 | if err != nil { | ||
424 | return | ||
425 | } | ||
426 | // resolvedDirPath will have been cleaned (no trailing path separators) so | ||
427 | // we can manually join it with the base path element. | ||
428 | resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath | ||
429 | if hasTrailingPathSeparator(path) && filepath.Base(path) != filepath.Base(resolvedPath) { | ||
430 | rebaseName = filepath.Base(path) | ||
431 | } | ||
432 | } | ||
433 | return resolvedPath, rebaseName, nil | ||
434 | } | ||
435 | |||
436 | // GetRebaseName normalizes and compares path and resolvedPath, | ||
437 | // return completed resolved path and rebased file name | ||
438 | func GetRebaseName(path, resolvedPath string) (string, string) { | ||
439 | // linkTarget will have been cleaned (no trailing path separators and dot) so | ||
440 | // we can manually join it with them | ||
441 | var rebaseName string | ||
442 | if specifiesCurrentDir(path) && !specifiesCurrentDir(resolvedPath) { | ||
443 | resolvedPath += string(filepath.Separator) + "." | ||
444 | } | ||
445 | |||
446 | if hasTrailingPathSeparator(path) && !hasTrailingPathSeparator(resolvedPath) { | ||
447 | resolvedPath += string(filepath.Separator) | ||
448 | } | ||
449 | |||
450 | if filepath.Base(path) != filepath.Base(resolvedPath) { | ||
451 | // In the case where the path had a trailing separator and a symlink | ||
452 | // evaluation has changed the last path component, we will need to | ||
453 | // rebase the name in the archive that is being copied to match the | ||
454 | // originally requested name. | ||
455 | rebaseName = filepath.Base(path) | ||
456 | } | ||
457 | return resolvedPath, rebaseName | ||
458 | } | ||