43bf65c174
The names of extended attributes are not completely freeform. Attributes are namespaced, and the kernel enforces (among other things) that only attributes whose names are prefixed with a valid namespace are permitted. The name of the attribute therefore needs to be known in order to diagnose issues with lsetxattr. Include the name of the extended attribute in the errors returned from the Lsetxattr and Lgetxattr so users and us can more easily troubleshoot xattr-related issues. Include the name in a separate rich-error field to provide code handling the error enough information to determine whether or not the failure can be ignored. Signed-off-by: Cory Snider <csnider@mirantis.com>
128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
package remotecontext // import "github.com/docker/docker/builder/remotecontext"
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/docker/docker/builder"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
|
"github.com/docker/docker/pkg/longpath"
|
|
"github.com/docker/docker/pkg/system"
|
|
"github.com/docker/docker/pkg/tarsum"
|
|
"github.com/moby/sys/symlink"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type archiveContext struct {
|
|
root string
|
|
sums tarsum.FileInfoSums
|
|
}
|
|
|
|
func (c *archiveContext) Close() error {
|
|
return os.RemoveAll(c.root)
|
|
}
|
|
|
|
func convertPathError(err error, cleanpath string) error {
|
|
switch err := err.(type) {
|
|
case *os.PathError:
|
|
err.Path = cleanpath
|
|
case *system.XattrError:
|
|
err.Path = cleanpath
|
|
}
|
|
return err
|
|
}
|
|
|
|
type modifiableContext interface {
|
|
builder.Source
|
|
// Remove deletes the entry specified by `path`.
|
|
// It is usual for directory entries to delete all its subentries.
|
|
Remove(path string) error
|
|
}
|
|
|
|
// FromArchive returns a build source from a tar stream.
|
|
//
|
|
// It extracts the tar stream to a temporary folder that is deleted as soon as
|
|
// the Context is closed.
|
|
// As the extraction happens, a tarsum is calculated for every file, and the set of
|
|
// all those sums then becomes the source of truth for all operations on this Context.
|
|
//
|
|
// Closing tarStream has to be done by the caller.
|
|
func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
|
root, err := longpath.MkdirTemp("", "docker-builder")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Assume local file system. Since it's coming from a tar file.
|
|
tsc := &archiveContext{root: root}
|
|
|
|
// Make sure we clean-up upon error. In the happy case the caller
|
|
// is expected to manage the clean-up
|
|
defer func() {
|
|
if err != nil {
|
|
tsc.Close()
|
|
}
|
|
}()
|
|
|
|
decompressedStream, err := archive.DecompressStream(tarStream)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sum, err := tarsum.NewTarSum(decompressedStream, true, tarsum.Version1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = chrootarchive.Untar(sum, root, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tsc.sums = sum.GetSums()
|
|
return tsc, nil
|
|
}
|
|
|
|
func (c *archiveContext) Root() string {
|
|
return c.root
|
|
}
|
|
|
|
func (c *archiveContext) Remove(path string) error {
|
|
_, fullpath, err := normalize(path, c.root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.RemoveAll(fullpath)
|
|
}
|
|
|
|
func (c *archiveContext) Hash(path string) (string, error) {
|
|
cleanpath, fullpath, err := normalize(path, c.root)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
rel, err := filepath.Rel(c.root, fullpath)
|
|
if err != nil {
|
|
return "", convertPathError(err, cleanpath)
|
|
}
|
|
|
|
// Use the checksum of the followed path(not the possible symlink) because
|
|
// this is the file that is actually copied.
|
|
if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil {
|
|
return tsInfo.Sum(), nil
|
|
}
|
|
// We set sum to path by default for the case where GetFile returns nil.
|
|
// The usual case is if relative path is empty.
|
|
return path, nil // backwards compat TODO: see if really needed
|
|
}
|
|
|
|
func normalize(path string, root string) (cleanPath, fullPath string, err error) {
|
|
cleanPath = filepath.Clean(string(filepath.Separator) + path)[1:]
|
|
fullPath, err = symlink.FollowSymlinkInScope(filepath.Join(root, path), root)
|
|
if err != nil {
|
|
return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
|
|
}
|
|
return
|
|
}
|