github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/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/symlink"
    36  	"github.com/docker/docker/pkg/system"
    37  	"github.com/docker/docker/pkg/tarsum"
    38  	"github.com/docker/docker/pkg/urlutil"
    39  	"github.com/docker/docker/registry"
    40  	"github.com/docker/docker/runconfig"
    41  )
    42  
    43  func (b *Builder) readContext(context io.Reader) error {
    44  	tmpdirPath, err := ioutil.TempDir("", "docker-build")
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	decompressedStream, err := archive.DecompressStream(context)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	if b.context, err = tarsum.NewTarSum(decompressedStream, true, tarsum.Version0); err != nil {
    55  		return err
    56  	}
    57  
    58  	if err := chrootarchive.Untar(b.context, tmpdirPath, nil); err != nil {
    59  		return err
    60  	}
    61  
    62  	b.contextPath = tmpdirPath
    63  	return nil
    64  }
    65  
    66  func (b *Builder) commit(id string, autoCmd *runconfig.Command, comment string) error {
    67  	if b.disableCommit {
    68  		return nil
    69  	}
    70  	if b.image == "" && !b.noBaseImage {
    71  		return fmt.Errorf("Please provide a source image with `from` prior to commit")
    72  	}
    73  	b.Config.Image = b.image
    74  	if id == "" {
    75  		cmd := b.Config.Cmd
    76  		b.Config.Cmd = runconfig.NewCommand("/bin/sh", "-c", "#(nop) "+comment)
    77  		defer func(cmd *runconfig.Command) { b.Config.Cmd = cmd }(cmd)
    78  
    79  		hit, err := b.probeCache()
    80  		if err != nil {
    81  			return err
    82  		}
    83  		if hit {
    84  			return nil
    85  		}
    86  
    87  		container, err := b.create()
    88  		if err != nil {
    89  			return err
    90  		}
    91  		id = container.ID
    92  
    93  		if err := container.Mount(); err != nil {
    94  			return err
    95  		}
    96  		defer container.Unmount()
    97  	}
    98  	container, err := b.Daemon.Get(id)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	// Note: Actually copy the struct
   104  	autoConfig := *b.Config
   105  	autoConfig.Cmd = autoCmd
   106  
   107  	// Commit the container
   108  	image, err := b.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	b.image = image.ID
   113  	return nil
   114  }
   115  
   116  type copyInfo struct {
   117  	origPath   string
   118  	destPath   string
   119  	hash       string
   120  	decompress bool
   121  	tmpDir     string
   122  }
   123  
   124  func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
   125  	if b.context == nil {
   126  		return fmt.Errorf("No context given. Impossible to use %s", cmdName)
   127  	}
   128  
   129  	if len(args) < 2 {
   130  		return fmt.Errorf("Invalid %s format - at least two arguments required", cmdName)
   131  	}
   132  
   133  	dest := args[len(args)-1] // last one is always the dest
   134  
   135  	copyInfos := []*copyInfo{}
   136  
   137  	b.Config.Image = b.image
   138  
   139  	defer func() {
   140  		for _, ci := range copyInfos {
   141  			if ci.tmpDir != "" {
   142  				os.RemoveAll(ci.tmpDir)
   143  			}
   144  		}
   145  	}()
   146  
   147  	// Loop through each src file and calculate the info we need to
   148  	// do the copy (e.g. hash value if cached).  Don't actually do
   149  	// the copy until we've looked at all src files
   150  	for _, orig := range args[0 : len(args)-1] {
   151  		if err := calcCopyInfo(
   152  			b,
   153  			cmdName,
   154  			&copyInfos,
   155  			orig,
   156  			dest,
   157  			allowRemote,
   158  			allowDecompression,
   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) 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 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  			calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression)
   365  		}
   366  		return nil
   367  	}
   368  
   369  	// Must be a dir or a file
   370  
   371  	if err := b.checkPathForAddition(origPath); err != nil {
   372  		return err
   373  	}
   374  	fi, _ := os.Stat(path.Join(b.contextPath, origPath))
   375  
   376  	ci := copyInfo{}
   377  	ci.origPath = origPath
   378  	ci.hash = origPath
   379  	ci.destPath = destPath
   380  	ci.decompress = allowDecompression
   381  	*cInfos = append(*cInfos, &ci)
   382  
   383  	// Deal with the single file case
   384  	if !fi.IsDir() {
   385  		// This will match first file in sums of the archive
   386  		fis := b.context.GetSums().GetFile(ci.origPath)
   387  		if fis != nil {
   388  			ci.hash = "file:" + fis.Sum()
   389  		}
   390  		return nil
   391  	}
   392  
   393  	// Must be a dir
   394  	var subfiles []string
   395  	absOrigPath := path.Join(b.contextPath, ci.origPath)
   396  
   397  	// Add a trailing / to make sure we only pick up nested files under
   398  	// the dir and not sibling files of the dir that just happen to
   399  	// start with the same chars
   400  	if !strings.HasSuffix(absOrigPath, "/") {
   401  		absOrigPath += "/"
   402  	}
   403  
   404  	// Need path w/o / too to find matching dir w/o trailing /
   405  	absOrigPathNoSlash := absOrigPath[:len(absOrigPath)-1]
   406  
   407  	for _, fileInfo := range b.context.GetSums() {
   408  		absFile := path.Join(b.contextPath, fileInfo.Name())
   409  		// Any file in the context that starts with the given path will be
   410  		// picked up and its hashcode used.  However, we'll exclude the
   411  		// root dir itself.  We do this for a coupel of reasons:
   412  		// 1 - ADD/COPY will not copy the dir itself, just its children
   413  		//     so there's no reason to include it in the hash calc
   414  		// 2 - the metadata on the dir will change when any child file
   415  		//     changes.  This will lead to a miss in the cache check if that
   416  		//     child file is in the .dockerignore list.
   417  		if strings.HasPrefix(absFile, absOrigPath) && absFile != absOrigPathNoSlash {
   418  			subfiles = append(subfiles, fileInfo.Sum())
   419  		}
   420  	}
   421  	sort.Strings(subfiles)
   422  	hasher := sha256.New()
   423  	hasher.Write([]byte(strings.Join(subfiles, ",")))
   424  	ci.hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
   425  
   426  	return nil
   427  }
   428  
   429  func ContainsWildcards(name string) bool {
   430  	for i := 0; i < len(name); i++ {
   431  		ch := name[i]
   432  		if ch == '\\' {
   433  			i++
   434  		} else if ch == '*' || ch == '?' || ch == '[' {
   435  			return true
   436  		}
   437  	}
   438  	return false
   439  }
   440  
   441  func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
   442  	remote, tag := parsers.ParseRepositoryTag(name)
   443  	if tag == "" {
   444  		tag = "latest"
   445  	}
   446  
   447  	pullRegistryAuth := b.AuthConfig
   448  	if len(b.ConfigFile.AuthConfigs) > 0 {
   449  		// The request came with a full auth config file, we prefer to use that
   450  		repoInfo, err := b.Daemon.RegistryService.ResolveRepository(remote)
   451  		if err != nil {
   452  			return nil, err
   453  		}
   454  		resolvedAuth := registry.ResolveAuthConfig(b.ConfigFile, repoInfo.Index)
   455  		pullRegistryAuth = &resolvedAuth
   456  	}
   457  
   458  	imagePullConfig := &graph.ImagePullConfig{
   459  		Parallel:   true,
   460  		AuthConfig: pullRegistryAuth,
   461  		OutStream:  ioutils.NopWriteCloser(b.OutOld),
   462  		Json:       b.StreamFormatter.Json(),
   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  		CpuQuota:   b.cpuQuota,
   558  		CpusetCpus: b.cpuSetCpus,
   559  		CpusetMems: b.cpuSetMems,
   560  		Memory:     b.memory,
   561  		MemorySwap: b.memorySwap,
   562  	}
   563  
   564  	config := *b.Config
   565  
   566  	// Create the container
   567  	c, warnings, err := b.Daemon.Create(b.Config, hostConfig, "")
   568  	if err != nil {
   569  		return nil, err
   570  	}
   571  	for _, warning := range warnings {
   572  		fmt.Fprintf(b.OutStream, " ---> [Warning] %s\n", warning)
   573  	}
   574  
   575  	b.TmpContainers[c.ID] = struct{}{}
   576  	fmt.Fprintf(b.OutStream, " ---> Running in %s\n", stringid.TruncateID(c.ID))
   577  
   578  	if config.Cmd.Len() > 0 {
   579  		// override the entry point that may have been picked up from the base image
   580  		s := config.Cmd.Slice()
   581  		c.Path = s[0]
   582  		c.Args = s[1:]
   583  	} else {
   584  		config.Cmd = runconfig.NewCommand()
   585  	}
   586  
   587  	return c, nil
   588  }
   589  
   590  func (b *Builder) run(c *daemon.Container) error {
   591  	var errCh chan error
   592  	if b.Verbose {
   593  		errCh = c.Attach(nil, b.OutStream, b.ErrStream)
   594  	}
   595  
   596  	//start the container
   597  	if err := c.Start(); err != nil {
   598  		return err
   599  	}
   600  
   601  	finished := make(chan struct{})
   602  	defer close(finished)
   603  	go func() {
   604  		select {
   605  		case <-b.cancelled:
   606  			logrus.Debugln("Build cancelled, killing container:", c.ID)
   607  			c.Kill()
   608  		case <-finished:
   609  		}
   610  	}()
   611  
   612  	if b.Verbose {
   613  		// Block on reading output from container, stop on err or chan closed
   614  		if err := <-errCh; err != nil {
   615  			return err
   616  		}
   617  	}
   618  
   619  	// Wait for it to finish
   620  	if ret, _ := c.WaitStop(-1 * time.Second); ret != 0 {
   621  		return &jsonmessage.JSONError{
   622  			Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret),
   623  			Code:    ret,
   624  		}
   625  	}
   626  
   627  	return nil
   628  }
   629  
   630  func (b *Builder) checkPathForAddition(orig string) error {
   631  	origPath := path.Join(b.contextPath, orig)
   632  	origPath, err := filepath.EvalSymlinks(origPath)
   633  	if err != nil {
   634  		if os.IsNotExist(err) {
   635  			return fmt.Errorf("%s: no such file or directory", orig)
   636  		}
   637  		return err
   638  	}
   639  	if !strings.HasPrefix(origPath, b.contextPath) {
   640  		return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
   641  	}
   642  	if _, err := os.Stat(origPath); err != nil {
   643  		if os.IsNotExist(err) {
   644  			return fmt.Errorf("%s: no such file or directory", orig)
   645  		}
   646  		return err
   647  	}
   648  	return nil
   649  }
   650  
   651  func (b *Builder) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
   652  	var (
   653  		err        error
   654  		destExists = true
   655  		origPath   = path.Join(b.contextPath, orig)
   656  		destPath   = path.Join(container.RootfsPath(), dest)
   657  	)
   658  
   659  	if destPath != container.RootfsPath() {
   660  		destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath())
   661  		if err != nil {
   662  			return err
   663  		}
   664  	}
   665  
   666  	// Preserve the trailing '/'
   667  	if strings.HasSuffix(dest, "/") || dest == "." {
   668  		destPath = destPath + "/"
   669  	}
   670  
   671  	destStat, err := os.Stat(destPath)
   672  	if err != nil {
   673  		if !os.IsNotExist(err) {
   674  			return err
   675  		}
   676  		destExists = false
   677  	}
   678  
   679  	fi, err := os.Stat(origPath)
   680  	if err != nil {
   681  		if os.IsNotExist(err) {
   682  			return fmt.Errorf("%s: no such file or directory", orig)
   683  		}
   684  		return err
   685  	}
   686  
   687  	if fi.IsDir() {
   688  		return copyAsDirectory(origPath, destPath, destExists)
   689  	}
   690  
   691  	// If we are adding a remote file (or we've been told not to decompress), do not try to untar it
   692  	if decompress {
   693  		// First try to unpack the source as an archive
   694  		// to support the untar feature we need to clean up the path a little bit
   695  		// because tar is very forgiving.  First we need to strip off the archive's
   696  		// filename from the path but this is only added if it does not end in / .
   697  		tarDest := destPath
   698  		if strings.HasSuffix(tarDest, "/") {
   699  			tarDest = filepath.Dir(destPath)
   700  		}
   701  
   702  		// try to successfully untar the orig
   703  		if err := chrootarchive.UntarPath(origPath, tarDest); err == nil {
   704  			return nil
   705  		} else if err != io.EOF {
   706  			logrus.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err)
   707  		}
   708  	}
   709  
   710  	if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
   711  		return err
   712  	}
   713  	if err := chrootarchive.CopyWithTar(origPath, destPath); err != nil {
   714  		return err
   715  	}
   716  
   717  	resPath := destPath
   718  	if destExists && destStat.IsDir() {
   719  		resPath = path.Join(destPath, path.Base(origPath))
   720  	}
   721  
   722  	return fixPermissions(origPath, resPath, 0, 0, destExists)
   723  }
   724  
   725  func copyAsDirectory(source, destination string, destExisted bool) error {
   726  	if err := chrootarchive.CopyWithTar(source, destination); err != nil {
   727  		return err
   728  	}
   729  	return fixPermissions(source, destination, 0, 0, destExisted)
   730  }
   731  
   732  func fixPermissions(source, destination string, uid, gid int, destExisted bool) error {
   733  	// If the destination didn't already exist, or the destination isn't a
   734  	// directory, then we should Lchown the destination. Otherwise, we shouldn't
   735  	// Lchown the destination.
   736  	destStat, err := os.Stat(destination)
   737  	if err != nil {
   738  		// This should *never* be reached, because the destination must've already
   739  		// been created while untar-ing the context.
   740  		return err
   741  	}
   742  	doChownDestination := !destExisted || !destStat.IsDir()
   743  
   744  	// We Walk on the source rather than on the destination because we don't
   745  	// want to change permissions on things we haven't created or modified.
   746  	return filepath.Walk(source, func(fullpath string, info os.FileInfo, err error) error {
   747  		// Do not alter the walk root iff. it existed before, as it doesn't fall under
   748  		// the domain of "things we should chown".
   749  		if !doChownDestination && (source == fullpath) {
   750  			return nil
   751  		}
   752  
   753  		// Path is prefixed by source: substitute with destination instead.
   754  		cleaned, err := filepath.Rel(source, fullpath)
   755  		if err != nil {
   756  			return err
   757  		}
   758  
   759  		fullpath = path.Join(destination, cleaned)
   760  		return os.Lchown(fullpath, uid, gid)
   761  	})
   762  }
   763  
   764  func (b *Builder) clearTmp() {
   765  	for c := range b.TmpContainers {
   766  		tmp, err := b.Daemon.Get(c)
   767  		if err != nil {
   768  			fmt.Fprint(b.OutStream, err.Error())
   769  		}
   770  
   771  		if err := b.Daemon.Rm(tmp); err != nil {
   772  			fmt.Fprintf(b.OutStream, "Error removing intermediate container %s: %v\n", stringid.TruncateID(c), err)
   773  			return
   774  		}
   775  		b.Daemon.DeleteVolumes(tmp.VolumePaths())
   776  		delete(b.TmpContainers, c)
   777  		fmt.Fprintf(b.OutStream, "Removing intermediate container %s\n", stringid.TruncateID(c))
   778  	}
   779  }