| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | package buildah | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	"archive/tar" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-03-08 02:26:17 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:01:59 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	"github.com/containers/buildah/copier" | 
					
						
							| 
									
										
										
										
											2021-02-07 06:49:40 +08:00
										 |  |  | 	"github.com/containers/buildah/define" | 
					
						
							| 
									
										
										
										
											2019-03-25 18:23:56 +08:00
										 |  |  | 	"github.com/containers/buildah/pkg/chrootuser" | 
					
						
							| 
									
										
										
										
											2019-05-30 07:56:28 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/fileutils" | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/idtools" | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	"github.com/hashicorp/go-multierror" | 
					
						
							| 
									
										
										
										
											2021-01-05 05:44:30 +08:00
										 |  |  | 	rsystem "github.com/opencontainers/runc/libcontainer/system" | 
					
						
							| 
									
										
										
										
											2017-11-30 22:34:02 +08:00
										 |  |  | 	"github.com/opencontainers/runtime-spec/specs-go" | 
					
						
							| 
									
										
										
										
											2017-06-02 03:23:02 +08:00
										 |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2017-10-10 03:05:56 +08:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 22:52:52 +08:00
										 |  |  | // AddAndCopyOptions holds options for add and copy commands.
 | 
					
						
							| 
									
										
										
										
											2017-11-30 22:34:02 +08:00
										 |  |  | type AddAndCopyOptions struct { | 
					
						
							| 
									
										
										
										
											2018-06-08 22:52:52 +08:00
										 |  |  | 	// Chown is a spec for the user who should be given ownership over the
 | 
					
						
							|  |  |  | 	// newly-added content, potentially overriding permissions which would
 | 
					
						
							| 
									
										
										
										
											2020-08-21 23:42:49 +08:00
										 |  |  | 	// otherwise be set to 0:0.
 | 
					
						
							| 
									
										
										
										
											2017-11-30 22:34:02 +08:00
										 |  |  | 	Chown string | 
					
						
							| 
									
										
										
										
											2020-08-21 23:42:49 +08:00
										 |  |  | 	// PreserveOwnership, if Chown is not set, tells us to avoid setting
 | 
					
						
							|  |  |  | 	// ownership of copied items to 0:0, instead using whatever ownership
 | 
					
						
							| 
									
										
										
										
											2020-09-30 03:01:27 +08:00
										 |  |  | 	// information is already set.  Not meaningful for remote sources or
 | 
					
						
							|  |  |  | 	// local archives that we extract.
 | 
					
						
							| 
									
										
										
										
											2020-08-21 23:42:49 +08:00
										 |  |  | 	PreserveOwnership bool | 
					
						
							| 
									
										
										
										
											2018-06-08 22:52:52 +08:00
										 |  |  | 	// All of the data being copied will pass through Hasher, if set.
 | 
					
						
							|  |  |  | 	// If the sources are URLs or files, their contents will be passed to
 | 
					
						
							|  |  |  | 	// Hasher.
 | 
					
						
							|  |  |  | 	// If the sources include directory trees, Hasher will be passed
 | 
					
						
							|  |  |  | 	// tar-format archives of the directory trees.
 | 
					
						
							|  |  |  | 	Hasher io.Writer | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// Excludes is the contents of the .dockerignore file.
 | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	Excludes []string | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// ContextDir is the base directory for content being copied and
 | 
					
						
							|  |  |  | 	// Excludes patterns.
 | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	ContextDir string | 
					
						
							| 
									
										
										
										
											2019-05-30 12:22:53 +08:00
										 |  |  | 	// ID mapping options to use when contents to be copied are part of
 | 
					
						
							|  |  |  | 	// another container, and need ownerships to be mapped from the host to
 | 
					
						
							|  |  |  | 	// that container's values before copying them into the container.
 | 
					
						
							| 
									
										
										
										
											2021-02-07 06:49:40 +08:00
										 |  |  | 	IDMappingOptions *define.IDMappingOptions | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 	// DryRun indicates that the content should be digested, but not actually
 | 
					
						
							|  |  |  | 	// copied into the container.
 | 
					
						
							|  |  |  | 	DryRun bool | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// Clear the setuid bit on items being copied.  Has no effect on
 | 
					
						
							|  |  |  | 	// archives being extracted, where the bit is always preserved.
 | 
					
						
							|  |  |  | 	StripSetuidBit bool | 
					
						
							|  |  |  | 	// Clear the setgid bit on items being copied.  Has no effect on
 | 
					
						
							|  |  |  | 	// archives being extracted, where the bit is always preserved.
 | 
					
						
							|  |  |  | 	StripSetgidBit bool | 
					
						
							|  |  |  | 	// Clear the sticky bit on items being copied.  Has no effect on
 | 
					
						
							|  |  |  | 	// archives being extracted, where the bit is always preserved.
 | 
					
						
							|  |  |  | 	StripStickyBit bool | 
					
						
							| 
									
										
										
										
											2017-11-30 22:34:02 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | // sourceIsRemote returns true if "source" is a remote location.
 | 
					
						
							|  |  |  | func sourceIsRemote(source string) bool { | 
					
						
							|  |  |  | 	return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getURL writes a tar archive containing the named content
 | 
					
						
							| 
									
										
										
										
											2020-11-05 06:23:25 +08:00
										 |  |  | func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, writer io.Writer) error { | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	url, err := url.Parse(src) | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	response, err := http.Get(src) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-08-10 04:21:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	defer response.Body.Close() | 
					
						
							|  |  |  | 	// Figure out what to name the new content.
 | 
					
						
							|  |  |  | 	name := renameTarget | 
					
						
							|  |  |  | 	if name == "" { | 
					
						
							|  |  |  | 		name = path.Base(url.Path) | 
					
						
							| 
									
										
										
										
											2019-08-10 04:21:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// If there's a date on the content, use it.  If not, use the Unix epoch
 | 
					
						
							|  |  |  | 	// for compatibility.
 | 
					
						
							|  |  |  | 	date := time.Unix(0, 0).UTC() | 
					
						
							|  |  |  | 	lastModified := response.Header.Get("Last-Modified") | 
					
						
							|  |  |  | 	if lastModified != "" { | 
					
						
							|  |  |  | 		d, err := time.Parse(time.RFC1123, lastModified) | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 			return errors.Wrapf(err, "error parsing last-modified time") | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		date = d | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Figure out the size of the content.
 | 
					
						
							|  |  |  | 	size := response.ContentLength | 
					
						
							|  |  |  | 	responseBody := response.Body | 
					
						
							|  |  |  | 	if size < 0 { | 
					
						
							|  |  |  | 		// Create a temporary file and copy the content to it, so that
 | 
					
						
							|  |  |  | 		// we can figure out how much content there is.
 | 
					
						
							|  |  |  | 		f, err := ioutil.TempFile(mountpoint, "download") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error creating temporary file to hold %q", src) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer os.Remove(f.Name()) | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 		defer f.Close() | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		size, err = io.Copy(f, response.Body) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error writing %q to temporary file %q", src, f.Name()) | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		_, err = f.Seek(0, io.SeekStart) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error setting up to read %q from temporary file %q", src, f.Name()) | 
					
						
							| 
									
										
										
										
											2019-06-08 06:02:50 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		responseBody = f | 
					
						
							| 
									
										
										
										
											2017-03-28 15:01:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// Write the output archive.  Set permissions for compatibility.
 | 
					
						
							|  |  |  | 	tw := tar.NewWriter(writer) | 
					
						
							|  |  |  | 	defer tw.Close() | 
					
						
							| 
									
										
										
										
											2020-11-05 06:23:25 +08:00
										 |  |  | 	uid := 0 | 
					
						
							|  |  |  | 	gid := 0 | 
					
						
							|  |  |  | 	if chown != nil { | 
					
						
							|  |  |  | 		uid = chown.UID | 
					
						
							|  |  |  | 		gid = chown.GID | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	hdr := tar.Header{ | 
					
						
							|  |  |  | 		Typeflag: tar.TypeReg, | 
					
						
							|  |  |  | 		Name:     name, | 
					
						
							|  |  |  | 		Size:     size, | 
					
						
							| 
									
										
										
										
											2020-11-05 06:23:25 +08:00
										 |  |  | 		Uid:      uid, | 
					
						
							|  |  |  | 		Gid:      gid, | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		Mode:     0600, | 
					
						
							|  |  |  | 		ModTime:  date, | 
					
						
							| 
									
										
										
										
											2017-04-04 05:44:23 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	err = tw.WriteHeader(&hdr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "error writing header") | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	_, err = io.Copy(tw, responseBody) | 
					
						
							|  |  |  | 	return errors.Wrapf(err, "error writing content from %q to tar stream", src) | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | // includeDirectoryAnyway returns true if "path" is a prefix for an exception
 | 
					
						
							|  |  |  | // known to "pm".  If "path" is a directory that "pm" claims matches its list
 | 
					
						
							|  |  |  | // of patterns, but "pm"'s list of exclusions contains a pattern for which
 | 
					
						
							|  |  |  | // "path" is a prefix, then IncludeDirectoryAnyway() will return true.
 | 
					
						
							|  |  |  | // This is not always correct, because it relies on the directory part of any
 | 
					
						
							|  |  |  | // exception paths to be specified without wildcards.
 | 
					
						
							|  |  |  | func includeDirectoryAnyway(path string, pm *fileutils.PatternMatcher) bool { | 
					
						
							|  |  |  | 	if !pm.Exclusions() { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	prefix := strings.TrimPrefix(path, string(os.PathSeparator)) + string(os.PathSeparator) | 
					
						
							|  |  |  | 	for _, pattern := range pm.Patterns() { | 
					
						
							|  |  |  | 		if !pattern.Exclusion() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		spec := strings.TrimPrefix(pattern.String(), string(os.PathSeparator)) | 
					
						
							|  |  |  | 		if strings.HasPrefix(spec, prefix) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 01:48:23 +08:00
										 |  |  | // Add copies the contents of the specified sources into the container's root
 | 
					
						
							|  |  |  | // filesystem, optionally extracting contents of local files that look like
 | 
					
						
							|  |  |  | // non-empty archives.
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, sources ...string) error { | 
					
						
							| 
									
										
										
										
											2017-11-22 21:57:31 +08:00
										 |  |  | 	mountPoint, err := b.Mount(b.MountLabel) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:48:23 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-24 01:48:23 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err2 := b.Unmount(); err2 != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("error unmounting container: %v", err2) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	contextDir := options.ContextDir | 
					
						
							| 
									
										
										
										
											2020-09-16 22:52:01 +08:00
										 |  |  | 	currentDir := options.ContextDir | 
					
						
							|  |  |  | 	if options.ContextDir == "" { | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		contextDir = string(os.PathSeparator) | 
					
						
							| 
									
										
										
										
											2020-09-16 22:52:01 +08:00
										 |  |  | 		currentDir, err = os.Getwd() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error determining current working directory") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-17 05:19:29 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Figure out what sorts of sources we have.
 | 
					
						
							|  |  |  | 	var localSources, remoteSources []string | 
					
						
							| 
									
										
										
										
											2020-09-16 22:52:01 +08:00
										 |  |  | 	for i, src := range sources { | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		if sourceIsRemote(src) { | 
					
						
							|  |  |  | 			remoteSources = append(remoteSources, src) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2019-05-26 20:56:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-16 22:52:01 +08:00
										 |  |  | 		if !filepath.IsAbs(src) && options.ContextDir == "" { | 
					
						
							|  |  |  | 			sources[i] = filepath.Join(currentDir, src) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		localSources = append(localSources, sources[i]) | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check how many items our local source specs matched.  Each spec
 | 
					
						
							|  |  |  | 	// should have matched at least one item, otherwise we consider it an
 | 
					
						
							|  |  |  | 	// error.
 | 
					
						
							|  |  |  | 	var localSourceStats []*copier.StatsForGlob | 
					
						
							|  |  |  | 	if len(localSources) > 0 { | 
					
						
							|  |  |  | 		statOptions := copier.StatOptions{ | 
					
						
							|  |  |  | 			CheckForArchives: extract, | 
					
						
							| 
									
										
										
										
											2017-03-24 01:47:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		localSourceStats, err = copier.Stat(contextDir, contextDir, statOptions, localSources) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error checking on sources %v under %q", localSources, contextDir) | 
					
						
							| 
									
										
										
										
											2017-03-28 03:35:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	numLocalSourceItems := 0 | 
					
						
							|  |  |  | 	for _, localSourceStat := range localSourceStats { | 
					
						
							|  |  |  | 		if localSourceStat.Error != "" { | 
					
						
							|  |  |  | 			errorText := localSourceStat.Error | 
					
						
							|  |  |  | 			rel, err := filepath.Rel(contextDir, localSourceStat.Glob) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				errorText = fmt.Sprintf("%v; %s", err, errorText) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { | 
					
						
							|  |  |  | 				errorText = fmt.Sprintf("possible escaping context directory error: %s", errorText) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return errors.Errorf("error checking on source %v under %q: %v", localSourceStat.Glob, contextDir, errorText) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:49:38 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		if len(localSourceStat.Globbed) == 0 { | 
					
						
							|  |  |  | 			return errors.Wrapf(syscall.ENOENT, "error checking on source %v under %q: no glob matches", localSourceStat.Glob, contextDir) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		numLocalSourceItems += len(localSourceStat.Globbed) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:49:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	if numLocalSourceItems+len(remoteSources) == 0 { | 
					
						
							|  |  |  | 		return errors.Wrapf(syscall.ENOENT, "no sources %v found", sources) | 
					
						
							| 
									
										
										
										
											2017-03-08 02:26:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// Find out which user (and group) the destination should belong to.
 | 
					
						
							|  |  |  | 	var chownDirs, chownFiles *idtools.IDPair | 
					
						
							|  |  |  | 	var user specs.User | 
					
						
							|  |  |  | 	if options.Chown != "" { | 
					
						
							|  |  |  | 		user, _, err = b.user(mountPoint, options.Chown) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errors.Wrapf(err, "error looking up UID/GID for %q", options.Chown) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	chownDirs = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} | 
					
						
							|  |  |  | 	chownFiles = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} | 
					
						
							| 
									
										
										
										
											2020-08-21 23:42:49 +08:00
										 |  |  | 	if options.Chown == "" && options.PreserveOwnership { | 
					
						
							|  |  |  | 		chownDirs = nil | 
					
						
							|  |  |  | 		chownFiles = nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// If we have a single source archive to extract, or more than one
 | 
					
						
							|  |  |  | 	// source item, or the destination has a path separator at the end of
 | 
					
						
							|  |  |  | 	// it, and it's not a remote URL, the destination needs to be a
 | 
					
						
							|  |  |  | 	// directory.
 | 
					
						
							|  |  |  | 	if destination == "" || !filepath.IsAbs(destination) { | 
					
						
							|  |  |  | 		tmpDestination := filepath.Join(string(os.PathSeparator)+b.WorkDir(), destination) | 
					
						
							|  |  |  | 		if destination == "" || strings.HasSuffix(destination, string(os.PathSeparator)) { | 
					
						
							|  |  |  | 			destination = tmpDestination + string(os.PathSeparator) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			destination = tmpDestination | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	destMustBeDirectory := (len(sources) > 1) || strings.HasSuffix(destination, string(os.PathSeparator)) | 
					
						
							|  |  |  | 	destCanBeFile := false | 
					
						
							|  |  |  | 	if len(sources) == 1 { | 
					
						
							|  |  |  | 		if len(remoteSources) == 1 { | 
					
						
							|  |  |  | 			destCanBeFile = sourceIsRemote(sources[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(localSources) == 1 { | 
					
						
							|  |  |  | 			item := localSourceStats[0].Results[localSourceStats[0].Globbed[0]] | 
					
						
							|  |  |  | 			if item.IsDir || (item.IsArchive && extract) { | 
					
						
							|  |  |  | 				destMustBeDirectory = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if item.IsRegular { | 
					
						
							|  |  |  | 				destCanBeFile = true | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// We care if the destination either doesn't exist, or exists and is a
 | 
					
						
							|  |  |  | 	// file.  If the source can be a single file, for those cases we treat
 | 
					
						
							|  |  |  | 	// the destination as a file rather than as a directory tree.
 | 
					
						
							|  |  |  | 	renameTarget := "" | 
					
						
							|  |  |  | 	extractDirectory := filepath.Join(mountPoint, destination) | 
					
						
							|  |  |  | 	statOptions := copier.StatOptions{ | 
					
						
							|  |  |  | 		CheckForArchives: extract, | 
					
						
							| 
									
										
										
										
											2019-05-30 07:56:28 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	destStats, err := copier.Stat(mountPoint, filepath.Join(mountPoint, b.WorkDir()), statOptions, []string{extractDirectory}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err, "error checking on destination %v", extractDirectory) | 
					
						
							| 
									
										
										
										
											2020-01-09 00:02:05 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	if (len(destStats) == 0 || len(destStats[0].Globbed) == 0) && !destMustBeDirectory && destCanBeFile { | 
					
						
							|  |  |  | 		// destination doesn't exist - extract to parent and rename the incoming file to the destination's name
 | 
					
						
							|  |  |  | 		renameTarget = filepath.Base(extractDirectory) | 
					
						
							|  |  |  | 		extractDirectory = filepath.Dir(extractDirectory) | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-07 09:20:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// if the destination is a directory that doesn't yet exist, let's copy it.
 | 
					
						
							|  |  |  | 	newDestDirFound := false | 
					
						
							|  |  |  | 	if (len(destStats) == 1 || len(destStats[0].Globbed) == 0) && destMustBeDirectory && !destCanBeFile { | 
					
						
							|  |  |  | 		newDestDirFound = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	if len(destStats) == 1 && len(destStats[0].Globbed) == 1 && destStats[0].Results[destStats[0].Globbed[0]].IsRegular { | 
					
						
							|  |  |  | 		if destMustBeDirectory { | 
					
						
							|  |  |  | 			return errors.Errorf("destination %v already exists but is not a directory", destination) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// destination exists - it's a file, we need to extract to parent and rename the incoming file to the destination's name
 | 
					
						
							|  |  |  | 		renameTarget = filepath.Base(extractDirectory) | 
					
						
							|  |  |  | 		extractDirectory = filepath.Dir(extractDirectory) | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pm, err := fileutils.NewPatternMatcher(options.Excludes) | 
					
						
							| 
									
										
										
										
											2019-04-04 04:32:12 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		return errors.Wrapf(err, "error processing excludes list %v", options.Excludes) | 
					
						
							| 
									
										
										
										
											2019-04-04 04:32:12 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-30 07:56:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 	// Copy each source in turn.
 | 
					
						
							|  |  |  | 	var srcUIDMap, srcGIDMap []idtools.IDMap | 
					
						
							|  |  |  | 	if options.IDMappingOptions != nil { | 
					
						
							|  |  |  | 		srcUIDMap, srcGIDMap = convertRuntimeIDMaps(options.IDMappingOptions.UIDMap, options.IDMappingOptions.GIDMap) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	destUIDMap, destGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, src := range sources { | 
					
						
							|  |  |  | 		var multiErr *multierror.Error | 
					
						
							|  |  |  | 		var getErr, closeErr, renameErr, putErr error | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							|  |  |  | 		if sourceIsRemote(src) { | 
					
						
							|  |  |  | 			pipeReader, pipeWriter := io.Pipe() | 
					
						
							|  |  |  | 			wg.Add(1) | 
					
						
							|  |  |  | 			go func() { | 
					
						
							| 
									
										
										
										
											2020-11-05 06:23:25 +08:00
										 |  |  | 				getErr = getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter) | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				pipeWriter.Close() | 
					
						
							|  |  |  | 				wg.Done() | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			wg.Add(1) | 
					
						
							|  |  |  | 			go func() { | 
					
						
							|  |  |  | 				b.ContentDigester.Start("") | 
					
						
							|  |  |  | 				hashCloser := b.ContentDigester.Hash() | 
					
						
							|  |  |  | 				hasher := io.Writer(hashCloser) | 
					
						
							|  |  |  | 				if options.Hasher != nil { | 
					
						
							|  |  |  | 					hasher = io.MultiWriter(hasher, options.Hasher) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if options.DryRun { | 
					
						
							|  |  |  | 					_, putErr = io.Copy(hasher, pipeReader) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					putOptions := copier.PutOptions{ | 
					
						
							| 
									
										
										
										
											2021-01-05 05:44:30 +08:00
										 |  |  | 						UIDMap:        destUIDMap, | 
					
						
							|  |  |  | 						GIDMap:        destGIDMap, | 
					
						
							|  |  |  | 						ChownDirs:     nil, | 
					
						
							|  |  |  | 						ChmodDirs:     nil, | 
					
						
							|  |  |  | 						ChownFiles:    nil, | 
					
						
							|  |  |  | 						ChmodFiles:    nil, | 
					
						
							|  |  |  | 						IgnoreDevices: rsystem.RunningInUserNS(), | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				hashCloser.Close() | 
					
						
							|  |  |  | 				pipeReader.Close() | 
					
						
							|  |  |  | 				wg.Done() | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			wg.Wait() | 
					
						
							|  |  |  | 			if getErr != nil { | 
					
						
							|  |  |  | 				getErr = errors.Wrapf(getErr, "error reading %q", src) | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			if putErr != nil { | 
					
						
							|  |  |  | 				putErr = errors.Wrapf(putErr, "error storing %q", src) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:49:38 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			multiErr = multierror.Append(getErr, putErr) | 
					
						
							|  |  |  | 			if multiErr != nil && multiErr.ErrorOrNil() != nil { | 
					
						
							|  |  |  | 				if len(multiErr.Errors) > 1 { | 
					
						
							|  |  |  | 					return multiErr.ErrorOrNil() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return multiErr.Errors[0] | 
					
						
							| 
									
										
										
										
											2017-11-30 22:34:02 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		// Dig out the result of running glob+stat on this source spec.
 | 
					
						
							|  |  |  | 		var localSourceStat *copier.StatsForGlob | 
					
						
							|  |  |  | 		for _, st := range localSourceStats { | 
					
						
							|  |  |  | 			if st.Glob == src { | 
					
						
							|  |  |  | 				localSourceStat = st | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		if localSourceStat == nil { | 
					
						
							|  |  |  | 			return errors.Errorf("internal error: should have statted %s, but we didn't?", src) | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-30 07:56:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		// Iterate through every item that matched the glob.
 | 
					
						
							|  |  |  | 		itemsCopied := 0 | 
					
						
							|  |  |  | 		for _, glob := range localSourceStat.Globbed { | 
					
						
							|  |  |  | 			rel, err := filepath.Rel(contextDir, glob) | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 				return errors.Wrapf(err, "error computing path of %q relative to %q", glob, contextDir) | 
					
						
							| 
									
										
										
										
											2018-09-12 21:28:38 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { | 
					
						
							|  |  |  | 				return errors.Errorf("possible escaping context directory error: %q is outside of %q", glob, contextDir) | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			// Check for dockerignore-style exclusion of this item.
 | 
					
						
							|  |  |  | 			if rel != "." { | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 				excluded, err := pm.Matches(filepath.ToSlash(rel)) // nolint:staticcheck
 | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return errors.Wrapf(err, "error checking if %q(%q) is excluded", glob, rel) | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 				if excluded { | 
					
						
							|  |  |  | 					// non-directories that are excluded are excluded, no question, but
 | 
					
						
							|  |  |  | 					// directories can only be skipped if we don't have to allow for the
 | 
					
						
							|  |  |  | 					// possibility of finding things to include under them
 | 
					
						
							|  |  |  | 					globInfo := localSourceStat.Results[glob] | 
					
						
							|  |  |  | 					if !globInfo.IsDir || !includeDirectoryAnyway(rel, pm) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2021-02-07 09:20:48 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					// if the destination is a directory that doesn't yet exist, and is not excluded, let's copy it.
 | 
					
						
							|  |  |  | 					if newDestDirFound { | 
					
						
							|  |  |  | 						itemsCopied++ | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-03-20 02:28:54 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				// Make sure we don't trigger a "copied nothing" error for an empty context
 | 
					
						
							|  |  |  | 				// directory if we were told to copy the context directory itself.  We won't
 | 
					
						
							|  |  |  | 				// actually copy it, but we need to make sure that we don't produce an error
 | 
					
						
							|  |  |  | 				// due to potentially not having anything in the tarstream that we passed.
 | 
					
						
							|  |  |  | 				itemsCopied++ | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			st := localSourceStat.Results[glob] | 
					
						
							|  |  |  | 			pipeReader, pipeWriter := io.Pipe() | 
					
						
							|  |  |  | 			wg.Add(1) | 
					
						
							|  |  |  | 			go func() { | 
					
						
							|  |  |  | 				renamedItems := 0 | 
					
						
							|  |  |  | 				writer := io.WriteCloser(pipeWriter) | 
					
						
							|  |  |  | 				if renameTarget != "" { | 
					
						
							|  |  |  | 					writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) { | 
					
						
							|  |  |  | 						hdr.Name = renameTarget | 
					
						
							|  |  |  | 						renamedItems++ | 
					
						
							|  |  |  | 						return false, false, nil | 
					
						
							|  |  |  | 					}) | 
					
						
							| 
									
										
										
										
											2020-01-06 21:34:12 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 				writer = newTarFilterer(writer, func(hdr *tar.Header) (bool, bool, io.Reader) { | 
					
						
							|  |  |  | 					itemsCopied++ | 
					
						
							|  |  |  | 					return false, false, nil | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				getOptions := copier.GetOptions{ | 
					
						
							|  |  |  | 					UIDMap:         srcUIDMap, | 
					
						
							|  |  |  | 					GIDMap:         srcGIDMap, | 
					
						
							|  |  |  | 					Excludes:       options.Excludes, | 
					
						
							|  |  |  | 					ExpandArchives: extract, | 
					
						
							| 
									
										
										
										
											2020-09-30 03:01:27 +08:00
										 |  |  | 					ChownDirs:      chownDirs, | 
					
						
							|  |  |  | 					ChmodDirs:      nil, | 
					
						
							|  |  |  | 					ChownFiles:     chownFiles, | 
					
						
							|  |  |  | 					ChmodFiles:     nil, | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 					StripSetuidBit: options.StripSetuidBit, | 
					
						
							|  |  |  | 					StripSetgidBit: options.StripSetgidBit, | 
					
						
							|  |  |  | 					StripStickyBit: options.StripStickyBit, | 
					
						
							| 
									
										
										
										
											2020-01-06 21:34:12 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				getErr = copier.Get(contextDir, contextDir, getOptions, []string{glob}, writer) | 
					
						
							|  |  |  | 				closeErr = writer.Close() | 
					
						
							|  |  |  | 				if renameTarget != "" && renamedItems > 1 { | 
					
						
							|  |  |  | 					renameErr = errors.Errorf("internal error: renamed %d items when we expected to only rename 1", renamedItems) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				wg.Done() | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			wg.Add(1) | 
					
						
							|  |  |  | 			go func() { | 
					
						
							|  |  |  | 				if st.IsDir { | 
					
						
							|  |  |  | 					b.ContentDigester.Start("dir") | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					b.ContentDigester.Start("file") | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				hashCloser := b.ContentDigester.Hash() | 
					
						
							|  |  |  | 				hasher := io.Writer(hashCloser) | 
					
						
							|  |  |  | 				if options.Hasher != nil { | 
					
						
							|  |  |  | 					hasher = io.MultiWriter(hasher, options.Hasher) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if options.DryRun { | 
					
						
							|  |  |  | 					_, putErr = io.Copy(hasher, pipeReader) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					putOptions := copier.PutOptions{ | 
					
						
							| 
									
										
										
										
											2020-09-30 03:01:27 +08:00
										 |  |  | 						UIDMap:          destUIDMap, | 
					
						
							|  |  |  | 						GIDMap:          destGIDMap, | 
					
						
							|  |  |  | 						DefaultDirOwner: chownDirs, | 
					
						
							|  |  |  | 						DefaultDirMode:  nil, | 
					
						
							|  |  |  | 						ChownDirs:       nil, | 
					
						
							|  |  |  | 						ChmodDirs:       nil, | 
					
						
							|  |  |  | 						ChownFiles:      nil, | 
					
						
							|  |  |  | 						ChmodFiles:      nil, | 
					
						
							| 
									
										
										
										
											2021-01-05 05:44:30 +08:00
										 |  |  | 						IgnoreDevices:   rsystem.RunningInUserNS(), | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					putErr = copier.Put(mountPoint, extractDirectory, putOptions, io.TeeReader(pipeReader, hasher)) | 
					
						
							| 
									
										
										
										
											2017-07-17 22:42:58 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 				hashCloser.Close() | 
					
						
							|  |  |  | 				pipeReader.Close() | 
					
						
							|  |  |  | 				wg.Done() | 
					
						
							|  |  |  | 			}() | 
					
						
							|  |  |  | 			wg.Wait() | 
					
						
							|  |  |  | 			if getErr != nil { | 
					
						
							|  |  |  | 				getErr = errors.Wrapf(getErr, "error reading %q", src) | 
					
						
							| 
									
										
										
										
											2017-03-24 01:49:38 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 			if closeErr != nil { | 
					
						
							|  |  |  | 				closeErr = errors.Wrapf(closeErr, "error closing %q", src) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if renameErr != nil { | 
					
						
							|  |  |  | 				renameErr = errors.Wrapf(renameErr, "error renaming %q", src) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if putErr != nil { | 
					
						
							|  |  |  | 				putErr = errors.Wrapf(putErr, "error storing %q", src) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			multiErr = multierror.Append(getErr, closeErr, renameErr, putErr) | 
					
						
							|  |  |  | 			if multiErr != nil && multiErr.ErrorOrNil() != nil { | 
					
						
							|  |  |  | 				if len(multiErr.Errors) > 1 { | 
					
						
							|  |  |  | 					return multiErr.ErrorOrNil() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return multiErr.Errors[0] | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if itemsCopied == 0 { | 
					
						
							| 
									
										
										
										
											2020-10-06 05:43:49 +08:00
										 |  |  | 			return errors.Wrapf(syscall.ENOENT, "no items matching glob %q copied (%d filtered out)", localSourceStat.Glob, len(localSourceStat.Globbed)) | 
					
						
							| 
									
										
										
										
											2017-03-07 04:39:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-25 22:10:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // user returns the user (and group) information which the destination should belong to.
 | 
					
						
							|  |  |  | func (b *Builder) user(mountPoint string, userspec string) (specs.User, string, error) { | 
					
						
							|  |  |  | 	if userspec == "" { | 
					
						
							|  |  |  | 		userspec = b.User() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uid, gid, homeDir, err := chrootuser.GetUser(mountPoint, userspec) | 
					
						
							|  |  |  | 	u := specs.User{ | 
					
						
							|  |  |  | 		UID:      uid, | 
					
						
							|  |  |  | 		GID:      gid, | 
					
						
							|  |  |  | 		Username: userspec, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !strings.Contains(userspec, ":") { | 
					
						
							|  |  |  | 		groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) | 
					
						
							|  |  |  | 		if err2 != nil { | 
					
						
							|  |  |  | 			if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { | 
					
						
							|  |  |  | 				err = err2 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			u.AdditionalGids = groups | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return u, homeDir, err | 
					
						
							|  |  |  | } |