|
@@ -6,7 +6,6 @@ import (
|
|
"bytes"
|
|
"bytes"
|
|
"compress/bzip2"
|
|
"compress/bzip2"
|
|
"compress/gzip"
|
|
"compress/gzip"
|
|
- "errors"
|
|
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
@@ -31,10 +30,6 @@ type (
|
|
Compression int
|
|
Compression int
|
|
// WhiteoutFormat is the format of whiteouts unpacked
|
|
// WhiteoutFormat is the format of whiteouts unpacked
|
|
WhiteoutFormat int
|
|
WhiteoutFormat int
|
|
- // TarChownOptions wraps the chown options UID and GID.
|
|
|
|
- TarChownOptions struct {
|
|
|
|
- UID, GID int
|
|
|
|
- }
|
|
|
|
|
|
|
|
// TarOptions wraps the tar options.
|
|
// TarOptions wraps the tar options.
|
|
TarOptions struct {
|
|
TarOptions struct {
|
|
@@ -44,7 +39,7 @@ type (
|
|
NoLchown bool
|
|
NoLchown bool
|
|
UIDMaps []idtools.IDMap
|
|
UIDMaps []idtools.IDMap
|
|
GIDMaps []idtools.IDMap
|
|
GIDMaps []idtools.IDMap
|
|
- ChownOpts *TarChownOptions
|
|
|
|
|
|
+ ChownOpts idtools.IDPair
|
|
IncludeSourceDir bool
|
|
IncludeSourceDir bool
|
|
// WhiteoutFormat is the expected on disk format for whiteout files.
|
|
// WhiteoutFormat is the expected on disk format for whiteout files.
|
|
// This format will be converted to the standard format on pack
|
|
// This format will be converted to the standard format on pack
|
|
@@ -58,33 +53,26 @@ type (
|
|
RebaseNames map[string]string
|
|
RebaseNames map[string]string
|
|
InUserNS bool
|
|
InUserNS bool
|
|
}
|
|
}
|
|
-
|
|
|
|
- // Archiver allows the reuse of most utility functions of this package
|
|
|
|
- // with a pluggable Untar function. Also, to facilitate the passing of
|
|
|
|
- // specific id mappings for untar, an archiver can be created with maps
|
|
|
|
- // which will then be passed to Untar operations
|
|
|
|
- Archiver struct {
|
|
|
|
- Untar func(io.Reader, string, *TarOptions) error
|
|
|
|
- UIDMaps []idtools.IDMap
|
|
|
|
- GIDMaps []idtools.IDMap
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // breakoutError is used to differentiate errors related to breaking out
|
|
|
|
- // When testing archive breakout in the unit tests, this error is expected
|
|
|
|
- // in order for the test to pass.
|
|
|
|
- breakoutError error
|
|
|
|
)
|
|
)
|
|
|
|
|
|
-var (
|
|
|
|
- // ErrNotImplemented is the error message of function not implemented.
|
|
|
|
- ErrNotImplemented = errors.New("Function not implemented")
|
|
|
|
- defaultArchiver = &Archiver{Untar: Untar, UIDMaps: nil, GIDMaps: nil}
|
|
|
|
-)
|
|
|
|
|
|
+// Archiver allows the reuse of most utility functions of this package
|
|
|
|
+// with a pluggable Untar function. Also, to facilitate the passing of
|
|
|
|
+// specific id mappings for untar, an archiver can be created with maps
|
|
|
|
+// which will then be passed to Untar operations
|
|
|
|
+type Archiver struct {
|
|
|
|
+ Untar func(io.Reader, string, *TarOptions) error
|
|
|
|
+ IDMappings *idtools.IDMappings
|
|
|
|
+}
|
|
|
|
|
|
-const (
|
|
|
|
- // HeaderSize is the size in bytes of a tar header
|
|
|
|
- HeaderSize = 512
|
|
|
|
-)
|
|
|
|
|
|
+// NewDefaultArchiver returns a new Archiver without any IDMappings
|
|
|
|
+func NewDefaultArchiver() *Archiver {
|
|
|
|
+ return &Archiver{Untar: Untar, IDMappings: &idtools.IDMappings{}}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// breakoutError is used to differentiate errors related to breaking out
|
|
|
|
+// When testing archive breakout in the unit tests, this error is expected
|
|
|
|
+// in order for the test to pass.
|
|
|
|
+type breakoutError error
|
|
|
|
|
|
const (
|
|
const (
|
|
// Uncompressed represents the uncompressed.
|
|
// Uncompressed represents the uncompressed.
|
|
@@ -105,18 +93,6 @@ const (
|
|
OverlayWhiteoutFormat
|
|
OverlayWhiteoutFormat
|
|
)
|
|
)
|
|
|
|
|
|
-// IsArchive checks for the magic bytes of a tar or any supported compression
|
|
|
|
-// algorithm.
|
|
|
|
-func IsArchive(header []byte) bool {
|
|
|
|
- compression := DetectCompression(header)
|
|
|
|
- if compression != Uncompressed {
|
|
|
|
- return true
|
|
|
|
- }
|
|
|
|
- r := tar.NewReader(bytes.NewBuffer(header))
|
|
|
|
- _, err := r.Next()
|
|
|
|
- return err == nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// IsArchivePath checks if the (possibly compressed) file at the given path
|
|
// IsArchivePath checks if the (possibly compressed) file at the given path
|
|
// starts with a tar file header.
|
|
// starts with a tar file header.
|
|
func IsArchivePath(path string) bool {
|
|
func IsArchivePath(path string) bool {
|
|
@@ -496,7 +472,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions, inUserns bool) error {
|
|
|
|
|
|
+func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *idtools.IDPair, inUserns bool) error {
|
|
// hdr.Mode is in linux format, which we can use for sycalls,
|
|
// hdr.Mode is in linux format, which we can use for sycalls,
|
|
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
|
// but for os.Foo() calls we need the mode converted to os.FileMode,
|
|
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
|
// so use hdrInfo.Mode() (they differ for e.g. setuid bits)
|
|
@@ -576,7 +552,7 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
|
|
// Lchown is not supported on Windows.
|
|
// Lchown is not supported on Windows.
|
|
if Lchown && runtime.GOOS != "windows" {
|
|
if Lchown && runtime.GOOS != "windows" {
|
|
if chownOpts == nil {
|
|
if chownOpts == nil {
|
|
- chownOpts = &TarChownOptions{UID: hdr.Uid, GID: hdr.Gid}
|
|
|
|
|
|
+ chownOpts = &idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
|
|
}
|
|
}
|
|
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
|
if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil {
|
|
return err
|
|
return err
|
|
@@ -941,7 +917,7 @@ loop:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil {
|
|
|
|
|
|
+ if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, &options.ChownOpts, options.InUserNS); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1013,23 +989,13 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
defer archive.Close()
|
|
defer archive.Close()
|
|
-
|
|
|
|
- var options *TarOptions
|
|
|
|
- if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
|
|
|
|
- options = &TarOptions{
|
|
|
|
- UIDMaps: archiver.UIDMaps,
|
|
|
|
- GIDMaps: archiver.GIDMaps,
|
|
|
|
- }
|
|
|
|
|
|
+ options := &TarOptions{
|
|
|
|
+ UIDMaps: archiver.IDMappings.UIDs(),
|
|
|
|
+ GIDMaps: archiver.IDMappings.GIDs(),
|
|
}
|
|
}
|
|
return archiver.Untar(archive, dst, options)
|
|
return archiver.Untar(archive, dst, options)
|
|
}
|
|
}
|
|
|
|
|
|
-// 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 TarUntar(src, dst string) error {
|
|
|
|
- return defaultArchiver.TarUntar(src, dst)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
|
// UntarPath untar a file from path to a destination, src is the source tar file path.
|
|
func (archiver *Archiver) UntarPath(src, dst string) error {
|
|
func (archiver *Archiver) UntarPath(src, dst string) error {
|
|
archive, err := os.Open(src)
|
|
archive, err := os.Open(src)
|
|
@@ -1037,22 +1003,13 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
defer archive.Close()
|
|
defer archive.Close()
|
|
- var options *TarOptions
|
|
|
|
- if archiver.UIDMaps != nil || archiver.GIDMaps != nil {
|
|
|
|
- options = &TarOptions{
|
|
|
|
- UIDMaps: archiver.UIDMaps,
|
|
|
|
- GIDMaps: archiver.GIDMaps,
|
|
|
|
- }
|
|
|
|
|
|
+ options := &TarOptions{
|
|
|
|
+ UIDMaps: archiver.IDMappings.UIDs(),
|
|
|
|
+ GIDMaps: archiver.IDMappings.GIDs(),
|
|
}
|
|
}
|
|
return archiver.Untar(archive, dst, options)
|
|
return archiver.Untar(archive, dst, options)
|
|
}
|
|
}
|
|
|
|
|
|
-// UntarPath is a convenience function which looks for an archive
|
|
|
|
-// at filesystem path `src`, and unpacks it at `dst`.
|
|
|
|
-func UntarPath(src, dst string) error {
|
|
|
|
- return defaultArchiver.UntarPath(src, dst)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
|
// CopyWithTar creates a tar archive of filesystem path `src`, and
|
|
// unpacks it at filesystem path `dst`.
|
|
// unpacks it at filesystem path `dst`.
|
|
// The archive is streamed directly with fixed buffering and no
|
|
// The archive is streamed directly with fixed buffering and no
|
|
@@ -1069,27 +1026,19 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error {
|
|
// if this archiver is set up with ID mapping we need to create
|
|
// if this archiver is set up with ID mapping we need to create
|
|
// the new destination directory with the remapped root UID/GID pair
|
|
// the new destination directory with the remapped root UID/GID pair
|
|
// as owner
|
|
// as owner
|
|
- rootUID, rootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps)
|
|
|
|
|
|
+ rootIDs, err := archiver.IDMappings.RootPair()
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
// Create dst, copy src's content into it
|
|
// Create dst, copy src's content into it
|
|
logrus.Debugf("Creating dest directory: %s", dst)
|
|
logrus.Debugf("Creating dest directory: %s", dst)
|
|
- if err := idtools.MkdirAllNewAs(dst, 0755, rootUID, rootGID); err != nil {
|
|
|
|
|
|
+ if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
|
logrus.Debugf("Calling TarUntar(%s, %s)", src, dst)
|
|
return archiver.TarUntar(src, dst)
|
|
return archiver.TarUntar(src, dst)
|
|
}
|
|
}
|
|
|
|
|
|
-// 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 CopyWithTar(src, dst string) error {
|
|
|
|
- return defaultArchiver.CopyWithTar(src, dst)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
|
// CopyFileWithTar emulates the behavior of the 'cp' command-line
|
|
// for a single file. It copies a regular file from path `src` to
|
|
// for a single file. It copies a regular file from path `src` to
|
|
// path `dst`, and preserves all its metadata.
|
|
// path `dst`, and preserves all its metadata.
|
|
@@ -1131,26 +1080,24 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
|
hdr.Name = filepath.Base(dst)
|
|
hdr.Name = filepath.Base(dst)
|
|
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
|
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
|
|
|
|
|
- remappedRootUID, remappedRootGID, err := idtools.GetRootUIDGID(archiver.UIDMaps, archiver.GIDMaps)
|
|
|
|
|
|
+ remappedRootIDs, err := archiver.IDMappings.RootPair()
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
// only perform mapping if the file being copied isn't already owned by the
|
|
// only perform mapping if the file being copied isn't already owned by the
|
|
// uid or gid of the remapped root in the container
|
|
// uid or gid of the remapped root in the container
|
|
- if remappedRootUID != hdr.Uid {
|
|
|
|
- xUID, err := idtools.ToHost(hdr.Uid, archiver.UIDMaps)
|
|
|
|
|
|
+ if remappedRootIDs.UID != hdr.Uid {
|
|
|
|
+ hdr.Uid, err = archiver.IDMappings.UIDToHost(hdr.Uid)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
- hdr.Uid = xUID
|
|
|
|
}
|
|
}
|
|
- if remappedRootGID != hdr.Gid {
|
|
|
|
- xGID, err := idtools.ToHost(hdr.Gid, archiver.GIDMaps)
|
|
|
|
|
|
+ if remappedRootIDs.GID != hdr.Gid {
|
|
|
|
+ hdr.Gid, err = archiver.IDMappings.GIDToHost(hdr.Gid)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
- hdr.Gid = xGID
|
|
|
|
}
|
|
}
|
|
|
|
|
|
tw := tar.NewWriter(w)
|
|
tw := tar.NewWriter(w)
|
|
@@ -1176,18 +1123,6 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
-// 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.
|
|
|
|
-//
|
|
|
|
-// Destination handling is in an operating specific manner depending
|
|
|
|
-// where the daemon is running. If `dst` ends with a trailing slash
|
|
|
|
-// the final destination path will be `dst/base(src)` (Linux) or
|
|
|
|
-// `dst\base(src)` (Windows).
|
|
|
|
-func CopyFileWithTar(src, dst string) (err error) {
|
|
|
|
- return defaultArchiver.CopyFileWithTar(src, dst)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// cmdStream executes a command, and returns its stdout as a stream.
|
|
// cmdStream executes a command, and returns its stdout as a stream.
|
|
// If the command fails to run or doesn't complete successfully, an error
|
|
// If the command fails to run or doesn't complete successfully, an error
|
|
// will be returned, including anything written on stderr.
|
|
// will be returned, including anything written on stderr.
|