Tizen_4.0 base
[platform/upstream/docker-engine.git] / pkg / archive / archive.go
1 package archive
2
3 import (
4         "archive/tar"
5         "bufio"
6         "bytes"
7         "compress/bzip2"
8         "compress/gzip"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "os"
13         "os/exec"
14         "path/filepath"
15         "runtime"
16         "strings"
17         "syscall"
18
19         "github.com/Sirupsen/logrus"
20         "github.com/docker/docker/pkg/fileutils"
21         "github.com/docker/docker/pkg/idtools"
22         "github.com/docker/docker/pkg/ioutils"
23         "github.com/docker/docker/pkg/pools"
24         "github.com/docker/docker/pkg/promise"
25         "github.com/docker/docker/pkg/system"
26         "golang.org/x/sys/unix"
27 )
28
29 type (
30         // Compression is the state represents if compressed or not.
31         Compression int
32         // WhiteoutFormat is the format of whiteouts unpacked
33         WhiteoutFormat int
34
35         // TarOptions wraps the tar options.
36         TarOptions struct {
37                 IncludeFiles     []string
38                 ExcludePatterns  []string
39                 Compression      Compression
40                 NoLchown         bool
41                 UIDMaps          []idtools.IDMap
42                 GIDMaps          []idtools.IDMap
43                 ChownOpts        *idtools.IDPair
44                 IncludeSourceDir bool
45                 // WhiteoutFormat is the expected on disk format for whiteout files.
46                 // This format will be converted to the standard format on pack
47                 // and from the standard format on unpack.
48                 WhiteoutFormat WhiteoutFormat
49                 // When unpacking, specifies whether overwriting a directory with a
50                 // non-directory is allowed and vice versa.
51                 NoOverwriteDirNonDir bool
52                 // For each include when creating an archive, the included name will be
53                 // replaced with the matching name from this map.
54                 RebaseNames map[string]string
55                 InUserNS    bool
56         }
57 )
58
59 // Archiver allows the reuse of most utility functions of this package
60 // with a pluggable Untar function. Also, to facilitate the passing of
61 // specific id mappings for untar, an archiver can be created with maps
62 // which will then be passed to Untar operations
63 type Archiver struct {
64         Untar      func(io.Reader, string, *TarOptions) error
65         IDMappings *idtools.IDMappings
66 }
67
68 // NewDefaultArchiver returns a new Archiver without any IDMappings
69 func NewDefaultArchiver() *Archiver {
70         return &Archiver{Untar: Untar, IDMappings: &idtools.IDMappings{}}
71 }
72
73 // breakoutError is used to differentiate errors related to breaking out
74 // When testing archive breakout in the unit tests, this error is expected
75 // in order for the test to pass.
76 type breakoutError error
77
78 const (
79         // Uncompressed represents the uncompressed.
80         Uncompressed Compression = iota
81         // Bzip2 is bzip2 compression algorithm.
82         Bzip2
83         // Gzip is gzip compression algorithm.
84         Gzip
85         // Xz is xz compression algorithm.
86         Xz
87 )
88
89 const (
90         // AUFSWhiteoutFormat is the default format for whiteouts
91         AUFSWhiteoutFormat WhiteoutFormat = iota
92         // OverlayWhiteoutFormat formats whiteout according to the overlay
93         // standard.
94         OverlayWhiteoutFormat
95 )
96
97 // IsArchivePath checks if the (possibly compressed) file at the given path
98 // starts with a tar file header.
99 func IsArchivePath(path string) bool {
100         file, err := os.Open(path)
101         if err != nil {
102                 return false
103         }
104         defer file.Close()
105         rdr, err := DecompressStream(file)
106         if err != nil {
107                 return false
108         }
109         r := tar.NewReader(rdr)
110         _, err = r.Next()
111         return err == nil
112 }
113
114 // DetectCompression detects the compression algorithm of the source.
115 func DetectCompression(source []byte) Compression {
116         for compression, m := range map[Compression][]byte{
117                 Bzip2: {0x42, 0x5A, 0x68},
118                 Gzip:  {0x1F, 0x8B, 0x08},
119                 Xz:    {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00},
120         } {
121                 if len(source) < len(m) {
122                         logrus.Debug("Len too short")
123                         continue
124                 }
125                 if bytes.Equal(m, source[:len(m)]) {
126                         return compression
127                 }
128         }
129         return Uncompressed
130 }
131
132 func xzDecompress(archive io.Reader) (io.ReadCloser, <-chan struct{}, error) {
133         args := []string{"xz", "-d", "-c", "-q"}
134
135         return cmdStream(exec.Command(args[0], args[1:]...), archive)
136 }
137
138 // DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
139 func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
140         p := pools.BufioReader32KPool
141         buf := p.Get(archive)
142         bs, err := buf.Peek(10)
143         if err != nil && err != io.EOF {
144                 // Note: we'll ignore any io.EOF error because there are some odd
145                 // cases where the layer.tar file will be empty (zero bytes) and
146                 // that results in an io.EOF from the Peek() call. So, in those
147                 // cases we'll just treat it as a non-compressed stream and
148                 // that means just create an empty layer.
149                 // See Issue 18170
150                 return nil, err
151         }
152
153         compression := DetectCompression(bs)
154         switch compression {
155         case Uncompressed:
156                 readBufWrapper := p.NewReadCloserWrapper(buf, buf)
157                 return readBufWrapper, nil
158         case Gzip:
159                 gzReader, err := gzip.NewReader(buf)
160                 if err != nil {
161                         return nil, err
162                 }
163                 readBufWrapper := p.NewReadCloserWrapper(buf, gzReader)
164                 return readBufWrapper, nil
165         case Bzip2:
166                 bz2Reader := bzip2.NewReader(buf)
167                 readBufWrapper := p.NewReadCloserWrapper(buf, bz2Reader)
168                 return readBufWrapper, nil
169         case Xz:
170                 xzReader, chdone, err := xzDecompress(buf)
171                 if err != nil {
172                         return nil, err
173                 }
174                 readBufWrapper := p.NewReadCloserWrapper(buf, xzReader)
175                 return ioutils.NewReadCloserWrapper(readBufWrapper, func() error {
176                         <-chdone
177                         return readBufWrapper.Close()
178                 }), nil
179         default:
180                 return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
181         }
182 }
183
184 // CompressStream compresseses the dest with specified compression algorithm.
185 func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
186         p := pools.BufioWriter32KPool
187         buf := p.Get(dest)
188         switch compression {
189         case Uncompressed:
190                 writeBufWrapper := p.NewWriteCloserWrapper(buf, buf)
191                 return writeBufWrapper, nil
192         case Gzip:
193                 gzWriter := gzip.NewWriter(dest)
194                 writeBufWrapper := p.NewWriteCloserWrapper(buf, gzWriter)
195                 return writeBufWrapper, nil
196         case Bzip2, Xz:
197                 // archive/bzip2 does not support writing, and there is no xz support at all
198                 // However, this is not a problem as docker only currently generates gzipped tars
199                 return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
200         default:
201                 return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension())
202         }
203 }
204
205 // TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to
206 // modify the contents or header of an entry in the archive. If the file already
207 // exists in the archive the TarModifierFunc will be called with the Header and
208 // a reader which will return the files content. If the file does not exist both
209 // header and content will be nil.
210 type TarModifierFunc func(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error)
211
212 // ReplaceFileTarWrapper converts inputTarStream to a new tar stream. Files in the
213 // tar stream are modified if they match any of the keys in mods.
214 func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModifierFunc) io.ReadCloser {
215         pipeReader, pipeWriter := io.Pipe()
216
217         go func() {
218                 tarReader := tar.NewReader(inputTarStream)
219                 tarWriter := tar.NewWriter(pipeWriter)
220                 defer inputTarStream.Close()
221                 defer tarWriter.Close()
222
223                 modify := func(name string, original *tar.Header, modifier TarModifierFunc, tarReader io.Reader) error {
224                         header, data, err := modifier(name, original, tarReader)
225                         switch {
226                         case err != nil:
227                                 return err
228                         case header == nil:
229                                 return nil
230                         }
231
232                         header.Name = name
233                         header.Size = int64(len(data))
234                         if err := tarWriter.WriteHeader(header); err != nil {
235                                 return err
236                         }
237                         if len(data) != 0 {
238                                 if _, err := tarWriter.Write(data); err != nil {
239                                         return err
240                                 }
241                         }
242                         return nil
243                 }
244
245                 var err error
246                 var originalHeader *tar.Header
247                 for {
248                         originalHeader, err = tarReader.Next()
249                         if err == io.EOF {
250                                 break
251                         }
252                         if err != nil {
253                                 pipeWriter.CloseWithError(err)
254                                 return
255                         }
256
257                         modifier, ok := mods[originalHeader.Name]
258                         if !ok {
259                                 // No modifiers for this file, copy the header and data
260                                 if err := tarWriter.WriteHeader(originalHeader); err != nil {
261                                         pipeWriter.CloseWithError(err)
262                                         return
263                                 }
264                                 if _, err := pools.Copy(tarWriter, tarReader); err != nil {
265                                         pipeWriter.CloseWithError(err)
266                                         return
267                                 }
268                                 continue
269                         }
270                         delete(mods, originalHeader.Name)
271
272                         if err := modify(originalHeader.Name, originalHeader, modifier, tarReader); err != nil {
273                                 pipeWriter.CloseWithError(err)
274                                 return
275                         }
276                 }
277
278                 // Apply the modifiers that haven't matched any files in the archive
279                 for name, modifier := range mods {
280                         if err := modify(name, nil, modifier, nil); err != nil {
281                                 pipeWriter.CloseWithError(err)
282                                 return
283                         }
284                 }
285
286                 pipeWriter.Close()
287
288         }()
289         return pipeReader
290 }
291
292 // Extension returns the extension of a file that uses the specified compression algorithm.
293 func (compression *Compression) Extension() string {
294         switch *compression {
295         case Uncompressed:
296                 return "tar"
297         case Bzip2:
298                 return "tar.bz2"
299         case Gzip:
300                 return "tar.gz"
301         case Xz:
302                 return "tar.xz"
303         }
304         return ""
305 }
306
307 // FileInfoHeader creates a populated Header from fi.
308 // Compared to archive pkg this function fills in more information.
309 func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
310         hdr, err := tar.FileInfoHeader(fi, link)
311         if err != nil {
312                 return nil, err
313         }
314         hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
315         name, err = canonicalTarName(name, fi.IsDir())
316         if err != nil {
317                 return nil, fmt.Errorf("tar: cannot canonicalize path: %v", err)
318         }
319         hdr.Name = name
320         if err := setHeaderForSpecialDevice(hdr, name, fi.Sys()); err != nil {
321                 return nil, err
322         }
323         return hdr, nil
324 }
325
326 // ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
327 // to a tar header
328 func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
329         capability, _ := system.Lgetxattr(path, "security.capability")
330         if capability != nil {
331                 hdr.Xattrs = make(map[string]string)
332                 hdr.Xattrs["security.capability"] = string(capability)
333         }
334         return nil
335 }
336
337 type tarWhiteoutConverter interface {
338         ConvertWrite(*tar.Header, string, os.FileInfo) (*tar.Header, error)
339         ConvertRead(*tar.Header, string) (bool, error)
340 }
341
342 type tarAppender struct {
343         TarWriter *tar.Writer
344         Buffer    *bufio.Writer
345
346         // for hardlink mapping
347         SeenFiles  map[uint64]string
348         IDMappings *idtools.IDMappings
349
350         // For packing and unpacking whiteout files in the
351         // non standard format. The whiteout files defined
352         // by the AUFS standard are used as the tar whiteout
353         // standard.
354         WhiteoutConverter tarWhiteoutConverter
355 }
356
357 func newTarAppender(idMapping *idtools.IDMappings, writer io.Writer) *tarAppender {
358         return &tarAppender{
359                 SeenFiles:  make(map[uint64]string),
360                 TarWriter:  tar.NewWriter(writer),
361                 Buffer:     pools.BufioWriter32KPool.Get(nil),
362                 IDMappings: idMapping,
363         }
364 }
365
366 // canonicalTarName provides a platform-independent and consistent posix-style
367 //path for files and directories to be archived regardless of the platform.
368 func canonicalTarName(name string, isDir bool) (string, error) {
369         name, err := CanonicalTarNameForPath(name)
370         if err != nil {
371                 return "", err
372         }
373
374         // suffix with '/' for directories
375         if isDir && !strings.HasSuffix(name, "/") {
376                 name += "/"
377         }
378         return name, nil
379 }
380
381 // addTarFile adds to the tar archive a file from `path` as `name`
382 func (ta *tarAppender) addTarFile(path, name string) error {
383         fi, err := os.Lstat(path)
384         if err != nil {
385                 return err
386         }
387
388         var link string
389         if fi.Mode()&os.ModeSymlink != 0 {
390                 var err error
391                 link, err = os.Readlink(path)
392                 if err != nil {
393                         return err
394                 }
395         }
396
397         hdr, err := FileInfoHeader(name, fi, link)
398         if err != nil {
399                 return err
400         }
401         if err := ReadSecurityXattrToTarHeader(path, hdr); err != nil {
402                 return err
403         }
404
405         // if it's not a directory and has more than 1 link,
406         // it's hard linked, so set the type flag accordingly
407         if !fi.IsDir() && hasHardlinks(fi) {
408                 inode, err := getInodeFromStat(fi.Sys())
409                 if err != nil {
410                         return err
411                 }
412                 // a link should have a name that it links too
413                 // and that linked name should be first in the tar archive
414                 if oldpath, ok := ta.SeenFiles[inode]; ok {
415                         hdr.Typeflag = tar.TypeLink
416                         hdr.Linkname = oldpath
417                         hdr.Size = 0 // This Must be here for the writer math to add up!
418                 } else {
419                         ta.SeenFiles[inode] = name
420                 }
421         }
422
423         //handle re-mapping container ID mappings back to host ID mappings before
424         //writing tar headers/files. We skip whiteout files because they were written
425         //by the kernel and already have proper ownership relative to the host
426         if !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IDMappings.Empty() {
427                 fileIDPair, err := getFileUIDGID(fi.Sys())
428                 if err != nil {
429                         return err
430                 }
431                 hdr.Uid, hdr.Gid, err = ta.IDMappings.ToContainer(fileIDPair)
432                 if err != nil {
433                         return err
434                 }
435         }
436
437         if ta.WhiteoutConverter != nil {
438                 wo, err := ta.WhiteoutConverter.ConvertWrite(hdr, path, fi)
439                 if err != nil {
440                         return err
441                 }
442
443                 // If a new whiteout file exists, write original hdr, then
444                 // replace hdr with wo to be written after. Whiteouts should
445                 // always be written after the original. Note the original
446                 // hdr may have been updated to be a whiteout with returning
447                 // a whiteout header
448                 if wo != nil {
449                         if err := ta.TarWriter.WriteHeader(hdr); err != nil {
450                                 return err
451                         }
452                         if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
453                                 return fmt.Errorf("tar: cannot use whiteout for non-empty file")
454                         }
455                         hdr = wo
456                 }
457         }
458
459         if err := ta.TarWriter.WriteHeader(hdr); err != nil {
460                 return err
461         }
462
463         if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 {
464                 // We use system.OpenSequential to ensure we use sequential file
465                 // access on Windows to avoid depleting the standby list.
466                 // On Linux, this equates to a regular os.Open.
467                 file, err := system.OpenSequential(path)
468                 if err != nil {
469                         return err
470                 }
471
472                 ta.Buffer.Reset(ta.TarWriter)
473                 defer ta.Buffer.Reset(nil)
474                 _, err = io.Copy(ta.Buffer, file)
475                 file.Close()
476                 if err != nil {
477                         return err
478                 }
479                 err = ta.Buffer.Flush()
480                 if err != nil {
481                         return err
482                 }
483         }
484
485         return nil
486 }
487
488 func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.IDPair, inUserns bool) error {
489         // hdr.Mode is in linux format, which we can use for sycalls,
490         // but for os.Foo() calls we need the mode converted to os.FileMode,
491         // so use hdrInfo.Mode() (they differ for e.g. setuid bits)
492         hdrInfo := hdr.FileInfo()
493
494         switch hdr.Typeflag {
495         case tar.TypeDir:
496                 // Create directory unless it exists as a directory already.
497                 // In that case we just want to merge the two
498                 if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
499                         if err := os.Mkdir(path, hdrInfo.Mode()); err != nil {
500                                 return err
501                         }
502                 }
503
504         case tar.TypeReg, tar.TypeRegA:
505                 // Source is regular file. We use system.OpenFileSequential to use sequential
506                 // file access to avoid depleting the standby list on Windows.
507                 // On Linux, this equates to a regular os.OpenFile
508                 file, err := system.OpenFileSequential(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode())
509                 if err != nil {
510                         return err
511                 }
512                 if _, err := io.Copy(file, reader); err != nil {
513                         file.Close()
514                         return err
515                 }
516
517                 if err := file.Sync(); err != nil {
518                         file.Close()
519                         return err
520                 }
521
522                 if err := unix.Fadvise(int(file.Fd()), 0, 0, unix.FADV_DONTNEED); err != nil {
523                         file.Close()
524                         return err
525                 }
526
527                 file.Close()
528
529         case tar.TypeBlock, tar.TypeChar:
530                 if inUserns { // cannot create devices in a userns
531                         return nil
532                 }
533                 // Handle this is an OS-specific way
534                 if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
535                         return err
536                 }
537
538         case tar.TypeFifo:
539                 // Handle this is an OS-specific way
540                 if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
541                         return err
542                 }
543
544         case tar.TypeLink:
545                 targetPath := filepath.Join(extractDir, hdr.Linkname)
546                 // check for hardlink breakout
547                 if !strings.HasPrefix(targetPath, extractDir) {
548                         return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname))
549                 }
550                 if err := os.Link(targetPath, path); err != nil {
551                         return err
552                 }
553
554         case tar.TypeSymlink:
555                 //      path                            -> hdr.Linkname = targetPath
556                 // e.g. /extractDir/path/to/symlink     -> ../2/file    = /extractDir/path/2/file
557                 targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname)
558
559                 // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because
560                 // that symlink would first have to be created, which would be caught earlier, at this very check:
561                 if !strings.HasPrefix(targetPath, extractDir) {
562                         return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname))
563                 }
564                 if err := os.Symlink(hdr.Linkname, path); err != nil {
565                         return err
566                 }
567
568         case tar.TypeXGlobalHeader:
569                 logrus.Debug("PAX Global Extended Headers found and ignored")
570                 return nil
571
572         default:
573                 return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
574         }
575
576         // Lchown is not supported on Windows.
577         if Lchown && runtime.GOOS != "windows" {
578                 if chownOpts == nil {
579                         chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
580                 }
581                 if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
582                         return err
583                 }
584         }
585
586         var errors []string
587         for key, value := range hdr.Xattrs {
588                 if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil {
589                         if err == syscall.ENOTSUP {
590                                 // We ignore errors here because not all graphdrivers support
591                                 // xattrs *cough* old versions of AUFS *cough*. However only
592                                 // ENOTSUP should be emitted in that case, otherwise we still
593                                 // bail.
594                                 errors = append(errors, err.Error())
595                                 continue
596                         }
597                         return err
598                 }
599
600         }
601
602         if len(errors) > 0 {
603                 logrus.WithFields(logrus.Fields{
604                         "errors": errors,
605                 }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them")
606         }
607
608         // There is no LChmod, so ignore mode for symlink. Also, this
609         // must happen after chown, as that can modify the file mode
610         if err := handleLChmod(hdr, path, hdrInfo); err != nil {
611                 return err
612         }
613
614         aTime := hdr.AccessTime
615         if aTime.Before(hdr.ModTime) {
616                 // Last access time should never be before last modified time.
617                 aTime = hdr.ModTime
618         }
619
620         // system.Chtimes doesn't support a NOFOLLOW flag atm
621         if hdr.Typeflag == tar.TypeLink {
622                 if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
623                         if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
624                                 return err
625                         }
626                 }
627         } else if hdr.Typeflag != tar.TypeSymlink {
628                 if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil {
629                         return err
630                 }
631         } else {
632                 ts := []syscall.Timespec{timeToTimespec(aTime), timeToTimespec(hdr.ModTime)}
633                 if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform {
634                         return err
635                 }
636         }
637
638         return nil
639 }
640
641 // Tar creates an archive from the directory at `path`, and returns it as a
642 // stream of bytes.
643 func Tar(path string, compression Compression) (io.ReadCloser, error) {
644         return TarWithOptions(path, &TarOptions{Compression: compression})
645 }
646
647 // TarWithOptions creates an archive from the directory at `path`, only including files whose relative
648 // paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
649 func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
650
651         // Fix the source path to work with long path names. This is a no-op
652         // on platforms other than Windows.
653         srcPath = fixVolumePathPrefix(srcPath)
654
655         pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns)
656         if err != nil {
657                 return nil, err
658         }
659
660         pipeReader, pipeWriter := io.Pipe()
661
662         compressWriter, err := CompressStream(pipeWriter, options.Compression)
663         if err != nil {
664                 return nil, err
665         }
666
667         go func() {
668                 ta := newTarAppender(
669                         idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
670                         compressWriter,
671                 )
672                 ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat)
673
674                 defer func() {
675                         // Make sure to check the error on Close.
676                         if err := ta.TarWriter.Close(); err != nil {
677                                 logrus.Errorf("Can't close tar writer: %s", err)
678                         }
679                         if err := compressWriter.Close(); err != nil {
680                                 logrus.Errorf("Can't close compress writer: %s", err)
681                         }
682                         if err := pipeWriter.Close(); err != nil {
683                                 logrus.Errorf("Can't close pipe writer: %s", err)
684                         }
685                 }()
686
687                 // this buffer is needed for the duration of this piped stream
688                 defer pools.BufioWriter32KPool.Put(ta.Buffer)
689
690                 // In general we log errors here but ignore them because
691                 // during e.g. a diff operation the container can continue
692                 // mutating the filesystem and we can see transient errors
693                 // from this
694
695                 stat, err := os.Lstat(srcPath)
696                 if err != nil {
697                         return
698                 }
699
700                 if !stat.IsDir() {
701                         // We can't later join a non-dir with any includes because the
702                         // 'walk' will error if "file/." is stat-ed and "file" is not a
703                         // directory. So, we must split the source path and use the
704                         // basename as the include.
705                         if len(options.IncludeFiles) > 0 {
706                                 logrus.Warn("Tar: Can't archive a file with includes")
707                         }
708
709                         dir, base := SplitPathDirEntry(srcPath)
710                         srcPath = dir
711                         options.IncludeFiles = []string{base}
712                 }
713
714                 if len(options.IncludeFiles) == 0 {
715                         options.IncludeFiles = []string{"."}
716                 }
717
718                 seen := make(map[string]bool)
719
720                 for _, include := range options.IncludeFiles {
721                         rebaseName := options.RebaseNames[include]
722
723                         walkRoot := getWalkRoot(srcPath, include)
724                         filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
725                                 if err != nil {
726                                         logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err)
727                                         return nil
728                                 }
729
730                                 relFilePath, err := filepath.Rel(srcPath, filePath)
731                                 if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
732                                         // Error getting relative path OR we are looking
733                                         // at the source directory path. Skip in both situations.
734                                         return nil
735                                 }
736
737                                 if options.IncludeSourceDir && include == "." && relFilePath != "." {
738                                         relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
739                                 }
740
741                                 skip := false
742
743                                 // If "include" is an exact match for the current file
744                                 // then even if there's an "excludePatterns" pattern that
745                                 // matches it, don't skip it. IOW, assume an explicit 'include'
746                                 // is asking for that file no matter what - which is true
747                                 // for some files, like .dockerignore and Dockerfile (sometimes)
748                                 if include != relFilePath {
749                                         skip, err = pm.Matches(relFilePath)
750                                         if err != nil {
751                                                 logrus.Errorf("Error matching %s: %v", relFilePath, err)
752                                                 return err
753                                         }
754                                 }
755
756                                 if skip {
757                                         // If we want to skip this file and its a directory
758                                         // then we should first check to see if there's an
759                                         // excludes pattern (e.g. !dir/file) that starts with this
760                                         // dir. If so then we can't skip this dir.
761
762                                         // Its not a dir then so we can just return/skip.
763                                         if !f.IsDir() {
764                                                 return nil
765                                         }
766
767                                         // No exceptions (!...) in patterns so just skip dir
768                                         if !pm.Exclusions() {
769                                                 return filepath.SkipDir
770                                         }
771
772                                         dirSlash := relFilePath + string(filepath.Separator)
773
774                                         for _, pat := range pm.Patterns() {
775                                                 if !pat.Exclusion() {
776                                                         continue
777                                                 }
778                                                 if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
779                                                         // found a match - so can't skip this dir
780                                                         return nil
781                                                 }
782                                         }
783
784                                         // No matching exclusion dir so just skip dir
785                                         return filepath.SkipDir
786                                 }
787
788                                 if seen[relFilePath] {
789                                         return nil
790                                 }
791                                 seen[relFilePath] = true
792
793                                 // Rename the base resource.
794                                 if rebaseName != "" {
795                                         var replacement string
796                                         if rebaseName != string(filepath.Separator) {
797                                                 // Special case the root directory to replace with an
798                                                 // empty string instead so that we don't end up with
799                                                 // double slashes in the paths.
800                                                 replacement = rebaseName
801                                         }
802
803                                         relFilePath = strings.Replace(relFilePath, include, replacement, 1)
804                                 }
805
806                                 if err := ta.addTarFile(filePath, relFilePath); err != nil {
807                                         logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
808                                         // if pipe is broken, stop writing tar stream to it
809                                         if err == io.ErrClosedPipe {
810                                                 return err
811                                         }
812                                 }
813                                 return nil
814                         })
815                 }
816         }()
817
818         return pipeReader, nil
819 }
820
821 // Unpack unpacks the decompressedArchive to dest with options.
822 func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) error {
823         tr := tar.NewReader(decompressedArchive)
824         trBuf := pools.BufioReader32KPool.Get(nil)
825         defer pools.BufioReader32KPool.Put(trBuf)
826
827         var dirs []*tar.Header
828         idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
829         rootIDs := idMappings.RootPair()
830         whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat)
831
832         // Iterate through the files in the archive.
833 loop:
834         for {
835                 hdr, err := tr.Next()
836                 if err == io.EOF {
837                         // end of tar archive
838                         break
839                 }
840                 if err != nil {
841                         return err
842                 }
843
844                 // Normalize name, for safety and for a simple is-root check
845                 // This keeps "../" as-is, but normalizes "/../" to "/". Or Windows:
846                 // This keeps "..\" as-is, but normalizes "\..\" to "\".
847                 hdr.Name = filepath.Clean(hdr.Name)
848
849                 for _, exclude := range options.ExcludePatterns {
850                         if strings.HasPrefix(hdr.Name, exclude) {
851                                 continue loop
852                         }
853                 }
854
855                 // After calling filepath.Clean(hdr.Name) above, hdr.Name will now be in
856                 // the filepath format for the OS on which the daemon is running. Hence
857                 // the check for a slash-suffix MUST be done in an OS-agnostic way.
858                 if !strings.HasSuffix(hdr.Name, string(os.PathSeparator)) {
859                         // Not the root directory, ensure that the parent directory exists
860                         parent := filepath.Dir(hdr.Name)
861                         parentPath := filepath.Join(dest, parent)
862                         if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
863                                 err = idtools.MkdirAllAndChownNew(parentPath, 0777, rootIDs)
864                                 if err != nil {
865                                         return err
866                                 }
867                         }
868                 }
869
870                 path := filepath.Join(dest, hdr.Name)
871                 rel, err := filepath.Rel(dest, path)
872                 if err != nil {
873                         return err
874                 }
875                 if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
876                         return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
877                 }
878
879                 // If path exits we almost always just want to remove and replace it
880                 // The only exception is when it is a directory *and* the file from
881                 // the layer is also a directory. Then we want to merge them (i.e.
882                 // just apply the metadata from the layer).
883                 if fi, err := os.Lstat(path); err == nil {
884                         if options.NoOverwriteDirNonDir && fi.IsDir() && hdr.Typeflag != tar.TypeDir {
885                                 // If NoOverwriteDirNonDir is true then we cannot replace
886                                 // an existing directory with a non-directory from the archive.
887                                 return fmt.Errorf("cannot overwrite directory %q with non-directory %q", path, dest)
888                         }
889
890                         if options.NoOverwriteDirNonDir && !fi.IsDir() && hdr.Typeflag == tar.TypeDir {
891                                 // If NoOverwriteDirNonDir is true then we cannot replace
892                                 // an existing non-directory with a directory from the archive.
893                                 return fmt.Errorf("cannot overwrite non-directory %q with directory %q", path, dest)
894                         }
895
896                         if fi.IsDir() && hdr.Name == "." {
897                                 continue
898                         }
899
900                         if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
901                                 if err := os.RemoveAll(path); err != nil {
902                                         return err
903                                 }
904                         }
905                 }
906                 trBuf.Reset(tr)
907
908                 if err := remapIDs(idMappings, hdr); err != nil {
909                         return err
910                 }
911
912                 if whiteoutConverter != nil {
913                         writeFile, err := whiteoutConverter.ConvertRead(hdr, path)
914                         if err != nil {
915                                 return err
916                         }
917                         if !writeFile {
918                                 continue
919                         }
920                 }
921
922                 if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil {
923                         return err
924                 }
925
926                 // Directory mtimes must be handled at the end to avoid further
927                 // file creation in them to modify the directory mtime
928                 if hdr.Typeflag == tar.TypeDir {
929                         dirs = append(dirs, hdr)
930                 }
931         }
932
933         for _, hdr := range dirs {
934                 path := filepath.Join(dest, hdr.Name)
935
936                 if err := system.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
937                         return err
938                 }
939         }
940         return nil
941 }
942
943 // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
944 // and unpacks it into the directory at `dest`.
945 // The archive may be compressed with one of the following algorithms:
946 //  identity (uncompressed), gzip, bzip2, xz.
947 // FIXME: specify behavior when target path exists vs. doesn't exist.
948 func Untar(tarArchive io.Reader, dest string, options *TarOptions) error {
949         return untarHandler(tarArchive, dest, options, true)
950 }
951
952 // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive,
953 // and unpacks it into the directory at `dest`.
954 // The archive must be an uncompressed stream.
955 func UntarUncompressed(tarArchive io.Reader, dest string, options *TarOptions) error {
956         return untarHandler(tarArchive, dest, options, false)
957 }
958
959 // Handler for teasing out the automatic decompression
960 func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decompress bool) error {
961         if tarArchive == nil {
962                 return fmt.Errorf("Empty archive")
963         }
964         dest = filepath.Clean(dest)
965         if options == nil {
966                 options = &TarOptions{}
967         }
968         if options.ExcludePatterns == nil {
969                 options.ExcludePatterns = []string{}
970         }
971
972         r := tarArchive
973         if decompress {
974                 decompressedArchive, err := DecompressStream(tarArchive)
975                 if err != nil {
976                         return err
977                 }
978                 defer decompressedArchive.Close()
979                 r = decompressedArchive
980         }
981
982         return Unpack(r, dest, options)
983 }
984
985 // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
986 // If either Tar or Untar fails, TarUntar aborts and returns the error.
987 func (archiver *Archiver) TarUntar(src, dst string) error {
988         logrus.Debugf("TarUntar(%s %s)", src, dst)
989         archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed})
990         if err != nil {
991                 return err
992         }
993         defer archive.Close()
994         options := &TarOptions{
995                 UIDMaps: archiver.IDMappings.UIDs(),
996                 GIDMaps: archiver.IDMappings.GIDs(),
997         }
998         return archiver.Untar(archive, dst, options)
999 }
1000
1001 // UntarPath untar a file from path to a destination, src is the source tar file path.
1002 func (archiver *Archiver) UntarPath(src, dst string) error {
1003         archive, err := os.Open(src)
1004         if err != nil {
1005                 return err
1006         }
1007         defer archive.Close()
1008         options := &TarOptions{
1009                 UIDMaps: archiver.IDMappings.UIDs(),
1010                 GIDMaps: archiver.IDMappings.GIDs(),
1011         }
1012         return archiver.Untar(archive, dst, options)
1013 }
1014
1015 // CopyWithTar creates a tar archive of filesystem path `src`, and
1016 // unpacks it at filesystem path `dst`.
1017 // The archive is streamed directly with fixed buffering and no
1018 // intermediary disk IO.
1019 func (archiver *Archiver) CopyWithTar(src, dst string) error {
1020         srcSt, err := os.Stat(src)
1021         if err != nil {
1022                 return err
1023         }
1024         if !srcSt.IsDir() {
1025                 return archiver.CopyFileWithTar(src, dst)
1026         }
1027
1028         // if this archiver is set up with ID mapping we need to create
1029         // the new destination directory with the remapped root UID/GID pair
1030         // as owner
1031         rootIDs := archiver.IDMappings.RootPair()
1032         // Create dst, copy src's content into it
1033         logrus.Debugf("Creating dest directory: %s", dst)
1034         if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
1035                 return err
1036         }
1037         logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
1038         return archiver.TarUntar(src, dst)
1039 }
1040
1041 // CopyFileWithTar emulates the behavior of the 'cp' command-line
1042 // for a single file. It copies a regular file from path `src` to
1043 // path `dst`, and preserves all its metadata.
1044 func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
1045         logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
1046         srcSt, err := os.Stat(src)
1047         if err != nil {
1048                 return err
1049         }
1050
1051         if srcSt.IsDir() {
1052                 return fmt.Errorf("Can't copy a directory")
1053         }
1054
1055         // Clean up the trailing slash. This must be done in an operating
1056         // system specific manner.
1057         if dst[len(dst)-1] == os.PathSeparator {
1058                 dst = filepath.Join(dst, filepath.Base(src))
1059         }
1060         // Create the holding directory if necessary
1061         if err := system.MkdirAll(filepath.Dir(dst), 0700, ""); err != nil {
1062                 return err
1063         }
1064
1065         r, w := io.Pipe()
1066         errC := promise.Go(func() error {
1067                 defer w.Close()
1068
1069                 srcF, err := os.Open(src)
1070                 if err != nil {
1071                         return err
1072                 }
1073                 defer srcF.Close()
1074
1075                 hdr, err := tar.FileInfoHeader(srcSt, "")
1076                 if err != nil {
1077                         return err
1078                 }
1079                 hdr.Name = filepath.Base(dst)
1080                 hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
1081
1082                 if err := remapIDs(archiver.IDMappings, hdr); err != nil {
1083                         return err
1084                 }
1085
1086                 tw := tar.NewWriter(w)
1087                 defer tw.Close()
1088                 if err := tw.WriteHeader(hdr); err != nil {
1089                         return err
1090                 }
1091                 if _, err := io.Copy(tw, srcF); err != nil {
1092                         return err
1093                 }
1094                 return nil
1095         })
1096         defer func() {
1097                 if er := <-errC; err == nil && er != nil {
1098                         err = er
1099                 }
1100         }()
1101
1102         err = archiver.Untar(r, filepath.Dir(dst), nil)
1103         if err != nil {
1104                 r.CloseWithError(err)
1105         }
1106         return err
1107 }
1108
1109 func remapIDs(idMappings *idtools.IDMappings, hdr *tar.Header) error {
1110         ids, err := idMappings.ToHost(idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid})
1111         hdr.Uid, hdr.Gid = ids.UID, ids.GID
1112         return err
1113 }
1114
1115 // cmdStream executes a command, and returns its stdout as a stream.
1116 // If the command fails to run or doesn't complete successfully, an error
1117 // will be returned, including anything written on stderr.
1118 func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, <-chan struct{}, error) {
1119         chdone := make(chan struct{})
1120         cmd.Stdin = input
1121         pipeR, pipeW := io.Pipe()
1122         cmd.Stdout = pipeW
1123         var errBuf bytes.Buffer
1124         cmd.Stderr = &errBuf
1125
1126         // Run the command and return the pipe
1127         if err := cmd.Start(); err != nil {
1128                 return nil, nil, err
1129         }
1130
1131         // Copy stdout to the returned pipe
1132         go func() {
1133                 if err := cmd.Wait(); err != nil {
1134                         pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String()))
1135                 } else {
1136                         pipeW.Close()
1137                 }
1138                 close(chdone)
1139         }()
1140
1141         return pipeR, chdone, nil
1142 }
1143
1144 // NewTempArchive reads the content of src into a temporary file, and returns the contents
1145 // of that file as an archive. The archive can only be read once - as soon as reading completes,
1146 // the file will be deleted.
1147 func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) {
1148         f, err := ioutil.TempFile(dir, "")
1149         if err != nil {
1150                 return nil, err
1151         }
1152         if _, err := io.Copy(f, src); err != nil {
1153                 return nil, err
1154         }
1155         if _, err := f.Seek(0, 0); err != nil {
1156                 return nil, err
1157         }
1158         st, err := f.Stat()
1159         if err != nil {
1160                 return nil, err
1161         }
1162         size := st.Size()
1163         return &TempArchive{File: f, Size: size}, nil
1164 }
1165
1166 // TempArchive is a temporary archive. The archive can only be read once - as soon as reading completes,
1167 // the file will be deleted.
1168 type TempArchive struct {
1169         *os.File
1170         Size   int64 // Pre-computed from Stat().Size() as a convenience
1171         read   int64
1172         closed bool
1173 }
1174
1175 // Close closes the underlying file if it's still open, or does a no-op
1176 // to allow callers to try to close the TempArchive multiple times safely.
1177 func (archive *TempArchive) Close() error {
1178         if archive.closed {
1179                 return nil
1180         }
1181
1182         archive.closed = true
1183
1184         return archive.File.Close()
1185 }
1186
1187 func (archive *TempArchive) Read(data []byte) (int, error) {
1188         n, err := archive.File.Read(data)
1189         archive.read += int64(n)
1190         if err != nil || archive.read == archive.Size {
1191                 archive.Close()
1192                 os.Remove(archive.File.Name())
1193         }
1194         return n, err
1195 }