github.com/webwurst/docker@v1.7.0/builder/internals.go (about)

     1  package builder
     2  
     3  // internals for handling commands. Covers many areas and a lot of
     4  // non-contiguous functionality. Please read the comments.
     5  
     6  import (
     7  	"crypto/sha256"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/url"
    14  	"os"
    15  	"path"
    16  	"path/filepath"
    17  	"sort"
    18  	"strings"
    19  	"syscall"
    20  	"time"
    21  
    22  	"github.com/Sirupsen/logrus"
    23  	"github.com/docker/docker/builder/parser"
    24  	"github.com/docker/docker/daemon"
    25  	"github.com/docker/docker/graph"
    26  	imagepkg "github.com/docker/docker/image"
    27  	"github.com/docker/docker/pkg/archive"
    28  	"github.com/docker/docker/pkg/chrootarchive"
    29  	"github.com/docker/docker/pkg/httputils"
    30  	"github.com/docker/docker/pkg/ioutils"
    31  	"github.com/docker/docker/pkg/jsonmessage"
    32  	"github.com/docker/docker/pkg/parsers"
    33  	"github.com/docker/docker/pkg/progressreader"
    34  	"github.com/docker/docker/pkg/stringid"
    35  	"github.com/docker/docker/pkg/system"
    36  	"github.com/docker/docker/pkg/tarsum"
    37  	"github.com/docker/docker/pkg/urlutil"
    38  	"github.com/docker/docker/registry"
    39  	"github.com/docker/docker/runconfig"
    40  )
    41  
    42  func (b *Builder) readContext(context io.Reader) error {
    43  	tmpdirPath, err := ioutil.TempDir("", "docker-build")
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	decompressedStream, err := archive.DecompressStream(context)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	if b.context, err = tarsum.NewTarSum(decompressedStream, true, tarsum.Version0); err != nil {
    54  		return err
    55  	}
    56  
    57  	if err := chrootarchive.Untar(b.context, tmpdirPath, nil); err != nil {
    58  		return err
    59  	}
    60  
    61  	b.contextPath = tmpdirPath
    62  	return nil
    63  }
    64  
    65  func (b *Builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
    66  	if b.disableCommit {
    67  		return nil
    68  	}
    69  	if b.image == "" && !b.noBaseImage {
    70  		return fmt.Errorf("Please provide a source image with `from` prior to commit")
    71  	}
    72  	b.Config.Image = b.image
    73  	if id == "" {
    74  		cmd := b.Config.Cmd
    75  		b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
    76  		defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
    77  
    78  		hit, err := b.probeCache()
    79  		if err != nil {
    80  			return err
    81  		}
    82  		if hit {
    83  			return nil
    84  		}
    85  
    86  		container, err := b.create()
    87  		if err != nil {
    88  			return err
    89  		}
    90  		id = container.ID
    91  
    92  		if err := container.Mount(); err != nil {
    93  			return err
    94  		}
    95  		defer container.Unmount()
    96  	}
    97  	container, err := b.Daemon.Get(id)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	// Note: Actually copy the struct
   103  	autoConfig := *b.Config
   104  	autoConfig.Cmd = autoCmd
   105  
   106  	// Commit the container
   107  	image, err := b.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	b.image = image.ID
   112  	return nil
   113  }
   114  
   115  type copyInfo struct {
   116  	origPath   string
   117  	destPath   string
   118  	hash       string
   119  	decompress bool
   120  	tmpDir     string
   121  }
   122  
   123  func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
   124  	if b.context == nil {
   125  		return fmt.Errorf("No context given. Impossible to use %s", cmdName)
   126  	}
   127  
   128  	if len(args) < 2 {
   129  		return fmt.Errorf("Invalid %s format - at least two arguments required", cmdName)
   130  	}
   131  
   132  	dest := args[len(args)-1] // last one is always the dest
   133  
   134  	copyInfos := []*copyInfo{}
   135  
   136  	b.Config.Image = b.image
   137  
   138  	defer func() {
   139  		for _, ci := range copyInfos {
   140  			if ci.tmpDir != "" {
   141  				os.RemoveAll(ci.tmpDir)
   142  			}
   143  		}
   144  	}()
   145  
   146  	// Loop through each src file and calculate the info we need to
   147  	// do the copy (e.g. hash value if cached).  Don't actually do
   148  	// the copy until we've looked at all src files
   149  	for _, orig := range args[0 : len(args)-1] {
   150  		if err := calcCopyInfo(
   151  			b,
   152  			cmdName,
   153  			&copyInfos,
   154  			orig,
   155  			dest,
   156  			allowRemote,
   157  			allowDecompression,
   158  			true,
   159  		); err != nil {
   160  			return err
   161  		}
   162  	}
   163  
   164  	if len(copyInfos) == 0 {
   165  		return fmt.Errorf("No source files were specified")
   166  	}
   167  
   168  	if len(copyInfos) > 1 && !strings.HasSuffix(dest, "/") {
   169  		return fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName)
   170  	}
   171  
   172  	// For backwards compat, if there's just one CI then use it as the
   173  	// cache look-up string, otherwise hash 'em all into one
   174  	var srcHash string
   175  	var origPaths string
   176  
   177  	if len(copyInfos) == 1 {
   178  		srcHash = copyInfos[0].hash
   179  		origPaths = copyInfos[0].origPath
   180  	} else {
   181  		var hashs []string
   182  		var origs []string
   183  		for _, ci := range copyInfos {
   184  			hashs = append(hashs, ci.hash)
   185  			origs = append(origs, ci.origPath)
   186  		}
   187  		hasher := sha256.New()
   188  		hasher.Write([]byte(strings.Join(hashs, ",")))
   189  		srcHash = "multi:" + hex.EncodeToString(hasher.Sum(nil))
   190  		origPaths = strings.Join(origs, " ")
   191  	}
   192  
   193  	cmd := b.Config.Cmd
   194  	b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, srcHash, dest))
   195  	defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
   196  
   197  	hit, err := b.probeCache()
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	if hit {
   203  		return nil
   204  	}
   205  
   206  	container, _, err := b.Daemon.Create(b.Config, nil, "")
   207  	if err != nil {
   208  		return err
   209  	}
   210  	b.TmpContainers[container.ID] = struct{}{}
   211  
   212  	if err := container.Mount(); err != nil {
   213  		return err
   214  	}
   215  	defer container.Unmount()
   216  
   217  	for _, ci := range copyInfos {
   218  		if err := b.addContext(container, ci.origPath, ci.destPath, ci.decompress); err != nil {
   219  			return err
   220  		}
   221  	}
   222  
   223  	if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, origPaths, dest)); err != nil {
   224  		return err
   225  	}
   226  	return nil
   227  }
   228  
   229  func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath string, destPath string, allowRemote bool, allowDecompression bool, allowWildcards bool) error {
   230  
   231  	if origPath != "" && origPath[0] == '/' && len(origPath) > 1 {
   232  		origPath = origPath[1:]
   233  	}
   234  	origPath = strings.TrimPrefix(origPath, "./")
   235  
   236  	// Twiddle the destPath when its a relative path - meaning, make it
   237  	// relative to the WORKINGDIR
   238  	if !filepath.IsAbs(destPath) {
   239  		hasSlash := strings.HasSuffix(destPath, "/")
   240  		destPath = filepath.Join("/", b.Config.WorkingDir, destPath)
   241  
   242  		// Make sure we preserve any trailing slash
   243  		if hasSlash {
   244  			destPath += "/"
   245  		}
   246  	}
   247  
   248  	// In the remote/URL case, download it and gen its hashcode
   249  	if urlutil.IsURL(origPath) {
   250  		if !allowRemote {
   251  			return fmt.Errorf("Source can't be a URL for %s", cmdName)
   252  		}
   253  
   254  		ci := copyInfo{}
   255  		ci.origPath = origPath
   256  		ci.hash = origPath // default to this but can change
   257  		ci.destPath = destPath
   258  		ci.decompress = false
   259  		*cInfos = append(*cInfos, &ci)
   260  
   261  		// Initiate the download
   262  		resp, err := httputils.Download(ci.origPath)
   263  		if err != nil {
   264  			return err
   265  		}
   266  
   267  		// Create a tmp dir
   268  		tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote")
   269  		if err != nil {
   270  			return err
   271  		}
   272  		ci.tmpDir = tmpDirName
   273  
   274  		// Create a tmp file within our tmp dir
   275  		tmpFileName := path.Join(tmpDirName, "tmp")
   276  		tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
   277  		if err != nil {
   278  			return err
   279  		}
   280  
   281  		// Download and dump result to tmp file
   282  		if _, err := io.Copy(tmpFile, progressreader.New(progressreader.Config{
   283  			In:        resp.Body,
   284  			Out:       b.OutOld,
   285  			Formatter: b.StreamFormatter,
   286  			Size:      int(resp.ContentLength),
   287  			NewLines:  true,
   288  			ID:        "",
   289  			Action:    "Downloading",
   290  		})); err != nil {
   291  			tmpFile.Close()
   292  			return err
   293  		}
   294  		fmt.Fprintf(b.OutStream, "\n")
   295  		tmpFile.Close()
   296  
   297  		// Set the mtime to the Last-Modified header value if present
   298  		// Otherwise just remove atime and mtime
   299  		times := make([]syscall.Timespec, 2)
   300  
   301  		lastMod := resp.Header.Get("Last-Modified")
   302  		if lastMod != "" {
   303  			mTime, err := http.ParseTime(lastMod)
   304  			// If we can't parse it then just let it default to 'zero'
   305  			// otherwise use the parsed time value
   306  			if err == nil {
   307  				times[1] = syscall.NsecToTimespec(mTime.UnixNano())
   308  			}
   309  		}
   310  
   311  		if err := system.UtimesNano(tmpFileName, times); err != nil {
   312  			return err
   313  		}
   314  
   315  		ci.origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
   316  
   317  		// If the destination is a directory, figure out the filename.
   318  		if strings.HasSuffix(ci.destPath, "/") {
   319  			u, err := url.Parse(origPath)
   320  			if err != nil {
   321  				return err
   322  			}
   323  			path := u.Path
   324  			if strings.HasSuffix(path, "/") {
   325  				path = path[:len(path)-1]
   326  			}
   327  			parts := strings.Split(path, "/")
   328  			filename := parts[len(parts)-1]
   329  			if filename == "" {
   330  				return fmt.Errorf("cannot determine filename from url: %s", u)
   331  			}
   332  			ci.destPath = ci.destPath + filename
   333  		}
   334  
   335  		// Calc the checksum, even if we're using the cache
   336  		r, err := archive.Tar(tmpFileName, archive.Uncompressed)
   337  		if err != nil {
   338  			return err
   339  		}
   340  		tarSum, err := tarsum.NewTarSum(r, true, tarsum.Version0)
   341  		if err != nil {
   342  			return err
   343  		}
   344  		if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
   345  			return err
   346  		}
   347  		ci.hash = tarSum.Sum(nil)
   348  		r.Close()
   349  
   350  		return nil
   351  	}
   352  
   353  	// Deal with wildcards
   354  	if allowWildcards && ContainsWildcards(origPath) {
   355  		for _, fileInfo := range b.context.GetSums() {
   356  			if fileInfo.Name() == "" {
   357  				continue
   358  			}
   359  			match, _ := path.Match(origPath, fileInfo.Name())
   360  			if !match {
   361  				continue
   362  			}
   363  
   364  			// Note we set allowWildcards to false in case the name has
   365  			// a * in it
   366  			calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression, false)
   367  		}
   368  		return nil
   369  	}
   370  
   371  	// Must be a dir or a file
   372  
   373  	if err := b.checkPathForAddition(origPath); err != nil {
   374  		return err
   375  	}
   376  	fi, _ := os.Stat(path.Join(b.contextPath, origPath))
   377  
   378  	ci := copyInfo{}
   379  	ci.origPath = origPath
   380  	ci.hash = origPath
   381  	ci.destPath = destPath
   382  	ci.decompress = allowDecompression
   383  	*cInfos = append(*cInfos, &ci)
   384  
   385  	// Deal with the single file case
   386  	if !fi.IsDir() {
   387  		// This will match first file in sums of the archive
   388  		fis := b.context.GetSums().GetFile(ci.origPath)
   389  		if fis != nil {
   390  			ci.hash = "file:" + fis.Sum()
   391  		}
   392  		return nil
   393  	}
   394  
   395  	// Must be a dir
   396  	var subfiles []string
   397  	absOrigPath := path.Join(b.contextPath, ci.origPath)
   398  
   399  	// Add a trailing / to make sure we only pick up nested files under
   400  	// the dir and not sibling files of the dir that just happen to
   401  	// start with the same chars
   402  	if !strings.HasSuffix(absOrigPath, "/") {
   403  		absOrigPath += "/"
   404  	}
   405  
   406  	// Need path w/o / too to find matching dir w/o trailing /
   407  	absOrigPathNoSlash := absOrigPath[:len(absOrigPath)-1]
   408  
   409  	for _, fileInfo := range b.context.GetSums() {
   410  		absFile := path.Join(b.contextPath, fileInfo.Name())
   411  		// Any file in the context that starts with the given path will be
   412  		// picked up and its hashcode used.  However, we'll exclude the
   413  		// root dir itself.  We do this for a coupel of reasons:
   414  		// 1 - ADD/COPY will not copy the dir itself, just its children
   415  		//     so there's no reason to include it in the hash calc
   416  		// 2 - the metadata on the dir will change when any child file
   417  		//     changes.  This will lead to a miss in the cache check if that
   418  		//     child file is in the .dockerignore list.
   419  		if strings.HasPrefix(absFile, absOrigPath) && absFile != absOrigPathNoSlash {
   420  			subfiles = append(subfiles, fileInfo.Sum())
   421  		}
   422  	}
   423  	sort.Strings(subfiles)
   424  	hasher := sha256.New()
   425  	hasher.Write([]byte(strings.Join(subfiles, ",")))
   426  	ci.hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
   427  
   428  	return nil
   429  }
   430  
   431  func ContainsWildcards(name string) bool {
   432  	for i := 0; i < len(name); i++ {
   433  		ch := name[i]
   434  		if ch == '\\' {
   435  			i++
   436  		} else if ch == '*' || ch == '?' || ch == '[' {
   437  			return true
   438  		}
   439  	}
   440  	return false
   441  }
   442  
   443  func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
   444  	remote, tag := parsers.ParseRepositoryTag(name)
   445  	if tag == "" {
   446  		tag = "latest"
   447  	}
   448  
   449  	pullRegistryAuth := b.AuthConfig
   450  	if len(b.ConfigFile.AuthConfigs) > 0 {
   451  		// The request came with a full auth config file, we prefer to use that
   452  		repoInfo, err := b.Daemon.RegistryService.ResolveRepository(remote)
   453  		if err != nil {
   454  			return nil, err
   455  		}
   456  		resolvedAuth := registry.ResolveAuthConfig(b.ConfigFile, repoInfo.Index)
   457  		pullRegistryAuth = &resolvedAuth
   458  	}
   459  
   460  	imagePullConfig := &graph.ImagePullConfig{
   461  		AuthConfig: pullRegistryAuth,
   462  		OutStream:  ioutils.NopWriteCloser(b.OutOld),
   463  	}
   464  
   465  	if err := b.Daemon.Repositories().Pull(remote, tag, imagePullConfig); err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	image, err := b.Daemon.Repositories().LookupImage(name)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	return image, nil
   475  }
   476  
   477  func (b *Builder) processImageFrom(img *imagepkg.Image) error {
   478  	b.image = img.ID
   479  
   480  	if img.Config != nil {
   481  		b.Config = img.Config
   482  	}
   483  
   484  	if len(b.Config.Env) == 0 {
   485  		b.Config.Env = append(b.Config.Env, "PATH="+daemon.DefaultPathEnv)
   486  	}
   487  
   488  	// Process ONBUILD triggers if they exist
   489  	if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
   490  		fmt.Fprintf(b.ErrStream, "# Executing %d build triggers\n", nTriggers)
   491  	}
   492  
   493  	// Copy the ONBUILD triggers, and remove them from the config, since the config will be committed.
   494  	onBuildTriggers := b.Config.OnBuild
   495  	b.Config.OnBuild = []string{}
   496  
   497  	// parse the ONBUILD triggers by invoking the parser
   498  	for stepN, step := range onBuildTriggers {
   499  		ast, err := parser.Parse(strings.NewReader(step))
   500  		if err != nil {
   501  			return err
   502  		}
   503  
   504  		for i, n := range ast.Children {
   505  			switch strings.ToUpper(n.Value) {
   506  			case "ONBUILD":
   507  				return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
   508  			case "MAINTAINER", "FROM":
   509  				return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value)
   510  			}
   511  
   512  			fmt.Fprintf(b.OutStream, "Trigger %d, %s\n", stepN, step)
   513  
   514  			if err := b.dispatch(i, n); err != nil {
   515  				return err
   516  			}
   517  		}
   518  	}
   519  
   520  	return nil
   521  }
   522  
   523  // probeCache checks to see if image-caching is enabled (`b.UtilizeCache`)
   524  // and if so attempts to look up the current `b.image` and `b.Config` pair
   525  // in the current server `b.Daemon`. If an image is found, probeCache returns
   526  // `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
   527  // is any error, it returns `(false, err)`.
   528  func (b *Builder) probeCache() (bool, error) {
   529  	if !b.UtilizeCache || b.cacheBusted {
   530  		return false, nil
   531  	}
   532  
   533  	cache, err := b.Daemon.ImageGetCached(b.image, b.Config)
   534  	if err != nil {
   535  		return false, err
   536  	}
   537  	if cache == nil {
   538  		logrus.Debugf("[BUILDER] Cache miss")
   539  		b.cacheBusted = true
   540  		return false, nil
   541  	}
   542  
   543  	fmt.Fprintf(b.OutStream, " ---> Using cache\n")
   544  	logrus.Debugf("[BUILDER] Use cached version")
   545  	b.image = cache.ID
   546  	return true, nil
   547  }
   548  
   549  func (b *Builder) create() (*daemon.Container, error) {
   550  	if b.image == "" && !b.noBaseImage {
   551  		return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
   552  	}
   553  	b.Config.Image = b.image
   554  
   555  	hostConfig := &runconfig.HostConfig{
   556  		CpuShares:    b.cpuShares,
   557  		CpuPeriod:    b.cpuPeriod,
   558  		CpuQuota:     b.cpuQuota,
   559  		CpusetCpus:   b.cpuSetCpus,
   560  		CpusetMems:   b.cpuSetMems,
   561  		CgroupParent: b.cgroupParent,
   562  		Memory:       b.memory,
   563  		MemorySwap:   b.memorySwap,
   564  		NetworkMode:  "bridge",
   565  	}
   566  
   567  	config := *b.Config
   568  
   569  	// Create the container
   570  	c, warnings, err := b.Daemon.Create(b.Config, hostConfig, "")
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	for _, warning := range warnings {
   575  		fmt.Fprintf(b.OutStream, " ---> [Warning] %s\n", warning)
   576  	}
   577  
   578  	b.TmpContainers[c.ID] = struct{}{}
   579  	fmt.Fprintf(b.OutStream, " ---> Running in %s\n", stringid.TruncateID(c.ID))
   580  
   581  	if config.Cmd.Len() > 0 {
   582  		// override the entry point that may have been picked up from the base image
   583  		s := config.Cmd.Slice()
   584  		c.Path = s[0]
   585  		c.Args = s[1:]
   586  	} else {
   587  		config.Cmd = runconfig.NewCommand()
   588  	}
   589  
   590  	return c, nil
   591  }
   592  
   593  func (b *Builder) run(c *daemon.Container) error {
   594  	var errCh chan error
   595  	if b.Verbose {
   596  		errCh = c.Attach(nil, b.OutStream, b.ErrStream)
   597  	}
   598  
   599  	//start the container
   600  	if err := c.Start(); err != nil {
   601  		return err
   602  	}
   603  
   604  	finished := make(chan struct{})
   605  	defer close(finished)
   606  	go func() {
   607  		select {
   608  		case <-b.cancelled:
   609  			logrus.Debugln("Build cancelled, killing container:", c.ID)
   610  			c.Kill()
   611  		case <-finished:
   612  		}
   613  	}()
   614  
   615  	if b.Verbose {
   616  		// Block on reading output from container, stop on err or chan closed
   617  		if err := <-errCh; err != nil {
   618  			return err
   619  		}
   620  	}
   621  
   622  	// Wait for it to finish
   623  	if ret, _ := c.WaitStop(-1 * time.Second); ret != 0 {
   624  		return &jsonmessage.JSONError{
   625  			Message: fmt.Sprintf("The command '%s' returned a non-zero code: %d", b.Config.Cmd.ToString(), ret),
   626  			Code:    ret,
   627  		}
   628  	}
   629  
   630  	return nil
   631  }
   632  
   633  func (b *Builder) checkPathForAddition(orig string) error {
   634  	origPath := path.Join(b.contextPath, orig)
   635  	origPath, err := filepath.EvalSymlinks(origPath)
   636  	if err != nil {
   637  		if os.IsNotExist(err) {
   638  			return fmt.Errorf("%s: no such file or directory", orig)
   639  		}
   640  		return err
   641  	}
   642  	if !strings.HasPrefix(origPath, b.contextPath) {
   643  		return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
   644  	}
   645  	if _, err := os.Stat(origPath); err != nil {
   646  		if os.IsNotExist(err) {
   647  			return fmt.Errorf("%s: no such file or directory", orig)
   648  		}
   649  		return err
   650  	}
   651  	return nil
   652  }
   653  
   654  func (b *Builder) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
   655  	var (
   656  		err        error
   657  		destExists = true
   658  		origPath   = path.Join(b.contextPath, orig)
   659  		destPath   string
   660  	)
   661  
   662  	destPath, err = container.GetResourcePath(dest)
   663  	if err != nil {
   664  		return err
   665  	}
   666  
   667  	// Preserve the trailing '/'
   668  	if strings.HasSuffix(dest, "/") || dest == "." {
   669  		destPath = destPath + "/"
   670  	}
   671  
   672  	destStat, err := os.Stat(destPath)
   673  	if err != nil {
   674  		if !os.IsNotExist(err) {
   675  			return err
   676  		}
   677  		destExists = false
   678  	}
   679  
   680  	fi, err := os.Stat(origPath)
   681  	if err != nil {
   682  		if os.IsNotExist(err) {
   683  			return fmt.Errorf("%s: no such file or directory", orig)
   684  		}
   685  		return err
   686  	}
   687  
   688  	if fi.IsDir() {
   689  		return copyAsDirectory(origPath, destPath, destExists)
   690  	}
   691  
   692  	// If we are adding a remote file (or we've been told not to decompress), do not try to untar it
   693  	if decompress {
   694  		// First try to unpack the source as an archive
   695  		// to support the untar feature we need to clean up the path a little bit
   696  		// because tar is very forgiving.  First we need to strip off the archive's
   697  		// filename from the path but this is only added if it does not end in / .
   698  		tarDest := destPath
   699  		if strings.HasSuffix(tarDest, "/") {
   700  			tarDest = filepath.Dir(destPath)
   701  		}
   702  
   703  		// try to successfully untar the orig
   704  		if err := chrootarchive.UntarPath(origPath, tarDest); err == nil {
   705  			return nil
   706  		} else if err != io.EOF {
   707  			logrus.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err)
   708  		}
   709  	}
   710  
   711  	if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
   712  		return err
   713  	}
   714  	if err := chrootarchive.CopyWithTar(origPath, destPath); err != nil {
   715  		return err
   716  	}
   717  
   718  	resPath := destPath
   719  	if destExists && destStat.IsDir() {
   720  		resPath = path.Join(destPath, path.Base(origPath))
   721  	}
   722  
   723  	return fixPermissions(origPath, resPath, 0, 0, destExists)
   724  }
   725  
   726  func copyAsDirectory(source, destination string, destExisted bool) error {
   727  	if err := chrootarchive.CopyWithTar(source, destination); err != nil {
   728  		return err
   729  	}
   730  	return fixPermissions(source, destination, 0, 0, destExisted)
   731  }
   732  
   733  func fixPermissions(source, destination string, uid, gid int, destExisted bool) error {
   734  	// If the destination didn't already exist, or the destination isn't a
   735  	// directory, then we should Lchown the destination. Otherwise, we shouldn't
   736  	// Lchown the destination.
   737  	destStat, err := os.Stat(destination)
   738  	if err != nil {
   739  		// This should *never* be reached, because the destination must've already
   740  		// been created while untar-ing the context.
   741  		return err
   742  	}
   743  	doChownDestination := !destExisted || !destStat.IsDir()
   744  
   745  	// We Walk on the source rather than on the destination because we don't
   746  	// want to change permissions on things we haven't created or modified.
   747  	return filepath.Walk(source, func(fullpath string, info os.FileInfo, err error) error {
   748  		// Do not alter the walk root iff. it existed before, as it doesn't fall under
   749  		// the domain of "things we should chown".
   750  		if !doChownDestination && (source == fullpath) {
   751  			return nil
   752  		}
   753  
   754  		// Path is prefixed by source: substitute with destination instead.
   755  		cleaned, err := filepath.Rel(source, fullpath)
   756  		if err != nil {
   757  			return err
   758  		}
   759  
   760  		fullpath = path.Join(destination, cleaned)
   761  		return os.Lchown(fullpath, uid, gid)
   762  	})
   763  }
   764  
   765  func (b *Builder) clearTmp() {
   766  	for c := range b.TmpContainers {
   767  		rmConfig := &daemon.ContainerRmConfig{
   768  			ForceRemove:  true,
   769  			RemoveVolume: true,
   770  		}
   771  		if err := b.Daemon.ContainerRm(c, rmConfig); err != nil {
   772  			fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %v\n", stringid.TruncateID(c), err)
   773  			return
   774  		}
   775  		delete(b.TmpContainers, c)
   776  		fmt.Fprintf(b.OutStream, "Removing intermediate container %s\n", stringid.TruncateID(c))
   777  	}
   778  }