mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
098a44c07f
Finish the refactor which was partially completed with commit
34536c498d
, passing around IdentityMapping structs instead of pairs of
[]IDMap slices.
Existing code which uses []IDMap relies on zero-valued fields to be
valid, empty mappings. So in order to successfully finish the
refactoring without introducing bugs, their replacement therefore also
needs to have a useful zero value which represents an empty mapping.
Change IdentityMapping to be a pass-by-value type so that there are no
nil pointers to worry about.
The functionality provided by the deprecated NewIDMappingsFromMaps
function is required by unit tests to to construct arbitrary
IdentityMapping values. And the daemon will always need to access the
mappings to pass them to the Linux kernel. Accommodate these use cases
by exporting the struct fields instead. BuildKit currently depends on
the UIDs and GIDs methods so we cannot get rid of them yet.
Signed-off-by: Cory Snider <csnider@mirantis.com>
203 lines
5.6 KiB
Go
203 lines
5.6 KiB
Go
package containerfs // import "github.com/docker/docker/pkg/containerfs"
|
|
|
|
import (
|
|
"archive/tar"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/idtools"
|
|
"github.com/docker/docker/pkg/system"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// TarFunc provides a function definition for a custom Tar function
|
|
type TarFunc func(string, *archive.TarOptions) (io.ReadCloser, error)
|
|
|
|
// UntarFunc provides a function definition for a custom Untar function
|
|
type UntarFunc func(io.Reader, string, *archive.TarOptions) error
|
|
|
|
// Archiver provides a similar implementation of the archive.Archiver package with the rootfs abstraction
|
|
type Archiver struct {
|
|
SrcDriver Driver
|
|
DstDriver Driver
|
|
Tar TarFunc
|
|
Untar UntarFunc
|
|
IDMapping idtools.IdentityMapping
|
|
}
|
|
|
|
// TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other.
|
|
// If either Tar or Untar fails, TarUntar aborts and returns the error.
|
|
func (archiver *Archiver) TarUntar(src, dst string) error {
|
|
logrus.Debugf("TarUntar(%s %s)", src, dst)
|
|
tarArchive, err := archiver.Tar(src, &archive.TarOptions{Compression: archive.Uncompressed})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tarArchive.Close()
|
|
options := &archive.TarOptions{
|
|
IDMap: archiver.IDMapping,
|
|
}
|
|
return archiver.Untar(tarArchive, dst, options)
|
|
}
|
|
|
|
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
|
func (archiver *Archiver) UntarPath(src, dst string) error {
|
|
tarArchive, err := archiver.SrcDriver.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tarArchive.Close()
|
|
options := &archive.TarOptions{
|
|
IDMap: archiver.IDMapping,
|
|
}
|
|
return archiver.Untar(tarArchive, dst, options)
|
|
}
|
|
|
|
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
|
// unpacks it at filesystem path `dst`.
|
|
// The archive is streamed directly with fixed buffering and no
|
|
// intermediary disk IO.
|
|
func (archiver *Archiver) CopyWithTar(src, dst string) error {
|
|
srcSt, err := archiver.SrcDriver.Stat(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !srcSt.IsDir() {
|
|
return archiver.CopyFileWithTar(src, dst)
|
|
}
|
|
|
|
// if this archiver is set up with ID mapping we need to create
|
|
// the new destination directory with the remapped root UID/GID pair
|
|
// as owner
|
|
|
|
identity := idtools.Identity{UID: archiver.IDMapping.RootPair().UID, GID: archiver.IDMapping.RootPair().GID}
|
|
|
|
// Create dst, copy src's content into it
|
|
if err := idtools.MkdirAllAndChownNew(dst, 0755, identity); err != nil {
|
|
return err
|
|
}
|
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
|
return archiver.TarUntar(src, dst)
|
|
}
|
|
|
|
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
|
// for a single file. It copies a regular file from path `src` to
|
|
// path `dst`, and preserves all its metadata.
|
|
func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) {
|
|
logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst)
|
|
srcDriver := archiver.SrcDriver
|
|
dstDriver := archiver.DstDriver
|
|
|
|
srcSt, retErr := srcDriver.Stat(src)
|
|
if retErr != nil {
|
|
return retErr
|
|
}
|
|
|
|
if srcSt.IsDir() {
|
|
return errors.New("cannot copy a directory")
|
|
}
|
|
|
|
// Clean up the trailing slash. This must be done in an operating
|
|
// system specific manner.
|
|
if dst[len(dst)-1] == dstDriver.Separator() {
|
|
dst = dstDriver.Join(dst, srcDriver.Base(src))
|
|
}
|
|
|
|
// The original call was system.MkdirAll, which is just
|
|
// os.MkdirAll on not-Windows and changed for Windows.
|
|
if dstDriver.OS() == "windows" {
|
|
// Now we are WCOW
|
|
if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// We can just use the driver.MkdirAll function
|
|
if err := dstDriver.MkdirAll(dstDriver.Dir(dst), 0700); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
r, w := io.Pipe()
|
|
errC := make(chan error, 1)
|
|
|
|
go func() {
|
|
defer close(errC)
|
|
errC <- func() error {
|
|
defer w.Close()
|
|
|
|
srcF, err := srcDriver.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer srcF.Close()
|
|
|
|
hdr, err := archive.FileInfoHeaderNoLookups(srcSt, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hdr.Format = tar.FormatPAX
|
|
hdr.ModTime = hdr.ModTime.Truncate(time.Second)
|
|
hdr.AccessTime = time.Time{}
|
|
hdr.ChangeTime = time.Time{}
|
|
hdr.Name = dstDriver.Base(dst)
|
|
if dstDriver.OS() == "windows" {
|
|
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
|
} else {
|
|
hdr.Mode = int64(os.FileMode(hdr.Mode))
|
|
}
|
|
|
|
if err := remapIDs(archiver.IDMapping, hdr); err != nil {
|
|
return err
|
|
}
|
|
|
|
tw := tar.NewWriter(w)
|
|
defer tw.Close()
|
|
if err := tw.WriteHeader(hdr); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.Copy(tw, srcF); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}()
|
|
}()
|
|
defer func() {
|
|
if err := <-errC; retErr == nil && err != nil {
|
|
retErr = err
|
|
}
|
|
}()
|
|
|
|
retErr = archiver.Untar(r, dstDriver.Dir(dst), nil)
|
|
if retErr != nil {
|
|
r.CloseWithError(retErr)
|
|
}
|
|
return retErr
|
|
}
|
|
|
|
// IdentityMapping returns the IdentityMapping of the archiver.
|
|
func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping {
|
|
return archiver.IDMapping
|
|
}
|
|
|
|
func remapIDs(idMapping idtools.IdentityMapping, hdr *tar.Header) error {
|
|
ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
|
|
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
|
return err
|
|
}
|
|
|
|
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
|
// on the platform the archival is done.
|
|
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
|
// perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
|
|
permPart := perm & os.ModePerm
|
|
noPermPart := perm &^ os.ModePerm
|
|
// Add the x bit: make everything +x from windows
|
|
permPart |= 0111
|
|
permPart &= 0755
|
|
|
|
return noPermPart | permPart
|
|
}
|