remote copy

Implement `podman-remote cp` and break out the logic from the previously
added `pkg/copy` into it's basic building blocks and move them up into
the `ContainerEngine` interface and `cmd/podman`.

The `--pause` and `--extract` flags are now deprecated and turned into
nops.

Note that this commit is vendoring a non-release version of Buildah to
pull in updates to the copier package.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2020-12-11 11:00:25 +01:00
parent f56865879c
commit adcb3a7a60
32 changed files with 1247 additions and 1160 deletions

View File

@@ -178,7 +178,7 @@ gce_instance:
image_name: "${FEDORA_CACHE_IMAGE_NAME}"
image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
image_name: "${UBUNTU_CACHE_IMAGE_NAME}"
image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}"
# image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}"
# Separate scripts for separate outputs, makes debugging easier.
setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}'

View File

@@ -39,6 +39,14 @@ SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go cmd/buildah/*.go copier/*.g
LINTFLAGS ?=
ifeq ($(DEBUG), 1)
override GOGCFLAGS += -N -l
endif
# make all DEBUG=1
# Note: Uses the -N -l go compiler options to disable compiler optimizations
# and inlining. Using these build options allows you to subsequently
# use source debugging tools like delve.
all: bin/buildah bin/imgtype docs
# Update nix/nixpkgs.json its latest stable commit
@@ -56,7 +64,7 @@ static:
.PHONY: bin/buildah
bin/buildah: $(SOURCES)
$(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./cmd/buildah
$(GO_BUILD) $(BUILDAH_LDFLAGS) -gcflags "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah
.PHONY: buildah
buildah: bin/buildah

View File

@@ -10,6 +10,7 @@ import (
"net"
"os"
"os/user"
"path"
"path/filepath"
"strconv"
"strings"
@@ -202,11 +203,11 @@ type StatOptions struct {
// If root and directory are both not specified, the current root directory is
// used, and relative names in the globs list are treated as being relative to
// the current working directory.
// If root is specified and the current OS supports it, the stat() is performed
// in a chrooted context. If the directory is specified as an absolute path,
// it should either be the root directory or a subdirectory of the root
// directory. Otherwise, the directory is treated as a path relative to the
// root directory.
// If root is specified and the current OS supports it, and the calling process
// has the necessary privileges, the stat() is performed in a chrooted context.
// If the directory is specified as an absolute path, it should either be the
// root directory or a subdirectory of the root directory. Otherwise, the
// directory is treated as a path relative to the root directory.
// Relative names in the glob list are treated as being relative to the
// directory.
func Stat(root string, directory string, options StatOptions, globs []string) ([]*StatsForGlob, error) {
@@ -229,18 +230,19 @@ func Stat(root string, directory string, options StatOptions, globs []string) ([
// GetOptions controls parts of Get()'s behavior.
type GetOptions struct {
UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive
Excludes []string // contents to pretend don't exist, using the OS-specific path separator
ExpandArchives bool // extract the contents of named items that are archives
ChownDirs *idtools.IDPair // set ownership on directories. no effect on archives being extracted
ChmodDirs *os.FileMode // set permissions on directories. no effect on archives being extracted
ChownFiles *idtools.IDPair // set ownership of files. no effect on archives being extracted
ChmodFiles *os.FileMode // set permissions on files. no effect on archives being extracted
StripSetuidBit bool // strip the setuid bit off of items being copied. no effect on archives being extracted
StripSetgidBit bool // strip the setgid bit off of items being copied. no effect on archives being extracted
StripStickyBit bool // strip the sticky bit off of items being copied. no effect on archives being extracted
StripXattrs bool // don't record extended attributes of items being copied. no effect on archives being extracted
KeepDirectoryNames bool // don't strip the top directory's basename from the paths of items in subdirectories
UIDMap, GIDMap []idtools.IDMap // map from hostIDs to containerIDs in the output archive
Excludes []string // contents to pretend don't exist, using the OS-specific path separator
ExpandArchives bool // extract the contents of named items that are archives
ChownDirs *idtools.IDPair // set ownership on directories. no effect on archives being extracted
ChmodDirs *os.FileMode // set permissions on directories. no effect on archives being extracted
ChownFiles *idtools.IDPair // set ownership of files. no effect on archives being extracted
ChmodFiles *os.FileMode // set permissions on files. no effect on archives being extracted
StripSetuidBit bool // strip the setuid bit off of items being copied. no effect on archives being extracted
StripSetgidBit bool // strip the setgid bit off of items being copied. no effect on archives being extracted
StripStickyBit bool // strip the sticky bit off of items being copied. no effect on archives being extracted
StripXattrs bool // don't record extended attributes of items being copied. no effect on archives being extracted
KeepDirectoryNames bool // don't strip the top directory's basename from the paths of items in subdirectories
Rename map[string]string // rename items with the specified names, or under the specified names
}
// Get produces an archive containing items that match the specified glob
@@ -248,11 +250,11 @@ type GetOptions struct {
// If root and directory are both not specified, the current root directory is
// used, and relative names in the globs list are treated as being relative to
// the current working directory.
// If root is specified and the current OS supports it, the contents are read
// in a chrooted context. If the directory is specified as an absolute path,
// it should either be the root directory or a subdirectory of the root
// directory. Otherwise, the directory is treated as a path relative to the
// root directory.
// If root is specified and the current OS supports it, and the calling process
// has the necessary privileges, the contents are read in a chrooted context.
// If the directory is specified as an absolute path, it should either be the
// root directory or a subdirectory of the root directory. Otherwise, the
// directory is treated as a path relative to the root directory.
// Relative names in the glob list are treated as being relative to the
// directory.
func Get(root string, directory string, options GetOptions, globs []string, bulkWriter io.Writer) error {
@@ -278,25 +280,28 @@ func Get(root string, directory string, options GetOptions, globs []string, bulk
// PutOptions controls parts of Put()'s behavior.
type PutOptions struct {
UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk
DefaultDirOwner *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
DefaultDirMode *os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
ChownDirs *idtools.IDPair // set ownership of newly-created directories
ChmodDirs *os.FileMode // set permissions on newly-created directories
ChownFiles *idtools.IDPair // set ownership of newly-created files
ChmodFiles *os.FileMode // set permissions on newly-created files
StripXattrs bool // don't bother trying to set extended attributes of items being copied
IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes
UIDMap, GIDMap []idtools.IDMap // map from containerIDs to hostIDs when writing contents to disk
DefaultDirOwner *idtools.IDPair // set ownership of implicitly-created directories, default is ChownDirs, or 0:0 if ChownDirs not set
DefaultDirMode *os.FileMode // set permissions on implicitly-created directories, default is ChmodDirs, or 0755 if ChmodDirs not set
ChownDirs *idtools.IDPair // set ownership of newly-created directories
ChmodDirs *os.FileMode // set permissions on newly-created directories
ChownFiles *idtools.IDPair // set ownership of newly-created files
ChmodFiles *os.FileMode // set permissions on newly-created files
StripXattrs bool // don't bother trying to set extended attributes of items being copied
IgnoreXattrErrors bool // ignore any errors encountered when attempting to set extended attributes
NoOverwriteDirNonDir bool // instead of quietly overwriting directories with non-directories, return an error
Rename map[string]string // rename items with the specified names, or under the specified names
}
// Put extracts an archive from the bulkReader at the specified directory.
// If root and directory are both not specified, the current root directory is
// used.
// If root is specified and the current OS supports it, the contents are written
// in a chrooted context. If the directory is specified as an absolute path,
// it should either be the root directory or a subdirectory of the root
// directory. Otherwise, the directory is treated as a path relative to the
// root directory.
// If root is specified and the current OS supports it, and the calling process
// has the necessary privileges, the contents are written in a chrooted
// context. If the directory is specified as an absolute path, it should
// either be the root directory or a subdirectory of the root directory.
// Otherwise, the directory is treated as a path relative to the root
// directory.
func Put(root string, directory string, options PutOptions, bulkReader io.Reader) error {
req := request{
Request: requestPut,
@@ -325,11 +330,12 @@ type MkdirOptions struct {
// need to be created will be given the specified ownership and permissions.
// If root and directory are both not specified, the current root directory is
// used.
// If root is specified and the current OS supports it, the directory is
// created in a chrooted context. If the directory is specified as an absolute
// path, it should either be the root directory or a subdirectory of the root
// directory. Otherwise, the directory is treated as a path relative to the
// root directory.
// If root is specified and the current OS supports it, and the calling process
// has the necessary privileges, the directory is created in a chrooted
// context. If the directory is specified as an absolute path, it should
// either be the root directory or a subdirectory of the root directory.
// Otherwise, the directory is treated as a path relative to the root
// directory.
func Mkdir(root string, directory string, options MkdirOptions) error {
req := request{
Request: requestMkdir,
@@ -547,13 +553,13 @@ func copierWithSubprocess(bulkReader io.Reader, bulkWriter io.Writer, req reques
return nil, errors.Wrap(err, step)
}
if err = encoder.Encode(req); err != nil {
return killAndReturn(err, "error encoding request")
return killAndReturn(err, "error encoding request for copier subprocess")
}
if err = decoder.Decode(&resp); err != nil {
return killAndReturn(err, "error decoding response")
return killAndReturn(err, "error decoding response from copier subprocess")
}
if err = encoder.Encode(&request{Request: requestQuit}); err != nil {
return killAndReturn(err, "error encoding request")
return killAndReturn(err, "error encoding request for copier subprocess")
}
stdinWrite.Close()
stdinWrite = nil
@@ -626,7 +632,7 @@ func copierMain() {
// Read a request.
req := new(request)
if err := decoder.Decode(req); err != nil {
fmt.Fprintf(os.Stderr, "error decoding request: %v", err)
fmt.Fprintf(os.Stderr, "error decoding request from copier parent process: %v", err)
os.Exit(1)
}
if req.Request == requestQuit {
@@ -717,12 +723,12 @@ func copierMain() {
}
resp, cb, err := copierHandler(bulkReader, bulkWriter, *req)
if err != nil {
fmt.Fprintf(os.Stderr, "error handling request %#v: %v", *req, err)
fmt.Fprintf(os.Stderr, "error handling request %#v from copier parent process: %v", *req, err)
os.Exit(1)
}
// Encode the response.
if err := encoder.Encode(resp); err != nil {
fmt.Fprintf(os.Stderr, "error encoding response %#v: %v", *req, err)
fmt.Fprintf(os.Stderr, "error encoding response %#v for copier parent process: %v", *req, err)
os.Exit(1)
}
// If there's bulk data to transfer, run the callback to either
@@ -1118,6 +1124,34 @@ func copierHandlerGet(bulkWriter io.Writer, req request, pm *fileutils.PatternMa
return &response{Stat: statResponse.Stat, Get: getResponse{}}, cb, nil
}
func handleRename(rename map[string]string, name string) string {
if rename == nil {
return name
}
// header names always use '/', so use path instead of filepath to manipulate it
if directMapping, ok := rename[name]; ok {
return directMapping
}
prefix, remainder := path.Split(name)
for prefix != "" {
if mappedPrefix, ok := rename[prefix]; ok {
return path.Join(mappedPrefix, remainder)
}
if prefix[len(prefix)-1] == '/' {
if mappedPrefix, ok := rename[prefix[:len(prefix)-1]]; ok {
return path.Join(mappedPrefix, remainder)
}
}
newPrefix, middlePart := path.Split(prefix)
if newPrefix == prefix {
return name
}
prefix = newPrefix
remainder = path.Join(middlePart, remainder)
}
return name
}
func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath string, options GetOptions, tw *tar.Writer, hardlinkChecker *util.HardlinkChecker, idMappings *idtools.IDMappings) error {
// build the header using the name provided
hdr, err := tar.FileInfoHeader(srcfi, symlinkTarget)
@@ -1127,6 +1161,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
if name != "" {
hdr.Name = filepath.ToSlash(name)
}
if options.Rename != nil {
hdr.Name = handleRename(options.Rename, hdr.Name)
}
if options.StripSetuidBit {
hdr.Mode &^= cISUID
}
@@ -1164,6 +1201,9 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
tr := tar.NewReader(rc)
hdr, err := tr.Next()
for err == nil {
if options.Rename != nil {
hdr.Name = handleRename(options.Rename, hdr.Name)
}
if err = tw.WriteHeader(hdr); err != nil {
return errors.Wrapf(err, "error writing tar header from %q to pipe", contentPath)
}
@@ -1311,8 +1351,13 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
createFile := func(path string, tr *tar.Reader) (int64, error) {
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600)
if err != nil && os.IsExist(err) {
if err = os.Remove(path); err != nil {
return 0, errors.Wrapf(err, "copier: put: error removing file to be overwritten %q", path)
if req.PutOptions.NoOverwriteDirNonDir {
if st, err2 := os.Lstat(path); err2 == nil && st.IsDir() {
return 0, errors.Wrapf(err, "copier: put: error creating file at %q", path)
}
}
if err = os.RemoveAll(path); err != nil {
return 0, errors.Wrapf(err, "copier: put: error removing item to be overwritten %q", path)
}
f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600)
}
@@ -1360,6 +1405,14 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
tr := tar.NewReader(bulkReader)
hdr, err := tr.Next()
for err == nil {
if len(hdr.Name) == 0 {
// no name -> ignore the entry
hdr, err = tr.Next()
continue
}
if req.PutOptions.Rename != nil {
hdr.Name = handleRename(req.PutOptions.Rename, hdr.Name)
}
// figure out who should own this new item
if idMappings != nil && !idMappings.Empty() {
containerPair := idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid}
@@ -1412,35 +1465,70 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
}
case tar.TypeLink:
var linkTarget string
if req.PutOptions.Rename != nil {
hdr.Linkname = handleRename(req.PutOptions.Rename, hdr.Linkname)
}
if linkTarget, err = resolvePath(targetDirectory, filepath.Join(req.Root, filepath.FromSlash(hdr.Linkname)), nil); err != nil {
return errors.Errorf("error resolving hardlink target path %q under root %q", hdr.Linkname, req.Root)
}
if err = os.Link(linkTarget, path); err != nil && os.IsExist(err) {
if req.PutOptions.NoOverwriteDirNonDir {
if st, err := os.Lstat(path); err == nil && st.IsDir() {
break
}
}
if err = os.Remove(path); err == nil {
err = os.Link(linkTarget, path)
}
}
case tar.TypeSymlink:
// if req.PutOptions.Rename != nil {
// todo: the general solution requires resolving to an absolute path, handling
// renaming, and then possibly converting back to a relative symlink
// }
if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && os.IsExist(err) {
if req.PutOptions.NoOverwriteDirNonDir {
if st, err := os.Lstat(path); err == nil && st.IsDir() {
break
}
}
if err = os.Remove(path); err == nil {
err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path))
}
}
case tar.TypeChar:
if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) {
if req.PutOptions.NoOverwriteDirNonDir {
if st, err := os.Lstat(path); err == nil && st.IsDir() {
break
}
}
if err = os.Remove(path); err == nil {
err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor)))
}
}
case tar.TypeBlock:
if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) {
if req.PutOptions.NoOverwriteDirNonDir {
if st, err := os.Lstat(path); err == nil && st.IsDir() {
break
}
}
if err = os.Remove(path); err == nil {
err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor)))
}
}
case tar.TypeDir:
if err = os.Mkdir(path, 0700); err != nil && os.IsExist(err) {
err = nil
var st os.FileInfo
if st, err = os.Stat(path); err == nil && !st.IsDir() {
// it's not a directory, so remove it and mkdir
if err = os.Remove(path); err == nil {
err = os.Mkdir(path, 0700)
}
}
// either we removed it and retried, or it was a directory,
// in which case we want to just add the new stuff under it
}
// make a note of the directory's times. we
// might create items under it, which will
@@ -1453,6 +1541,11 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM
})
case tar.TypeFifo:
if err = mkfifo(path, 0600); err != nil && os.IsExist(err) {
if req.PutOptions.NoOverwriteDirNonDir {
if st, err := os.Lstat(path); err == nil && st.IsDir() {
break
}
}
if err = os.Remove(path); err == nil {
err = mkfifo(path, 0600)
}

View File

@@ -10,7 +10,7 @@ import (
"golang.org/x/sys/unix"
)
var canChroot = true
var canChroot = os.Getuid() == 0
func chroot(root string) (bool, error) {
if canChroot {

View File

@@ -5,10 +5,10 @@ go 1.12
require (
github.com/containerd/containerd v1.4.1 // indirect
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
github.com/containers/common v0.29.0
github.com/containers/common v0.31.0
github.com/containers/image/v5 v5.8.1
github.com/containers/ocicrypt v1.0.3
github.com/containers/storage v1.24.1
github.com/containers/storage v1.24.3
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/go-units v0.4.0
github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316
@@ -21,7 +21,7 @@ require (
github.com/moby/sys/mount v0.1.1 // indirect
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 // indirect
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.3
github.com/onsi/gomega v1.10.4
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/runc v1.0.0-rc91

View File

@@ -73,8 +73,8 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc=
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containers/common v0.29.0 h1:hTMC+urdkk5bKfhL/OgCixIX5xjJgQ2l2jPG745ECFQ=
github.com/containers/common v0.29.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
github.com/containers/common v0.31.0 h1:SRnjfoqbjfaojpY9YJq9JBPEslwB5hoXJbaE+5zMFwM=
github.com/containers/common v0.31.0/go.mod h1:yT4GTUHsKRmpaDb+mecXRnIMre7W3ZgwXqaYMywXlaA=
github.com/containers/image/v5 v5.8.1 h1:aHW8a/Kd0dTJ7PTL/fc6y12sJqHxWgqilu+XyHfjD8Q=
github.com/containers/image/v5 v5.8.1/go.mod h1:blOEFd/iFdeyh891ByhCVUc+xAcaI3gBegXECwz9UbQ=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
@@ -84,6 +84,8 @@ github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQ
github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI=
github.com/containers/storage v1.24.1 h1:1+f8fy6ly35c8SLet5jzZ8t0WJJs5+xSpfMAYw0R3kc=
github.com/containers/storage v1.24.1/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
github.com/containers/storage v1.24.3 h1:8UB4S62l4hrU6Yw3dbsLCJtLg7Ofo39IN2HdckBIX4E=
github.com/containers/storage v1.24.3/go.mod h1:0xJL06Dmd+ZYXIUdnBUPN0JnhHGgwMkLvnnAonJfWJU=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -303,6 +305,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -482,6 +486,8 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@@ -69,15 +69,35 @@ sudo apt-get update
sudo apt-get -y install buildah
```
The [Kubic project](https://build.opensuse.org/project/show/devel:kubic:libcontainers:stable)
provides packages for Debian 10. The Kubic packages for Debian Testing/Bullseye and Debian Unstable/Sid
have been discontinued to avoid
[conflicts](https://github.com/containers/buildah/issues/2797) with the official packages.
If you would prefer newer (though not as well-tested) packages,
the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah)
provides packages for Debian 10 and newer. The packages in Kubic project repos are more frequently
updated than the one in Debian's official repositories, due to how Debian works.
The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
CAUTION: On Debian 11 and newer, including Testing and Sid/Unstable, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo
OR the official Debian repos. Mixing and matching may lead to unpredictable situations including installation conflicts.
```bash
# Debian 10
echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_10/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install buildah
# Debian Testing
echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Testing/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install buildah
# Debian Sid/Unstable
echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install buildah
```
Caution: If you upgrade from Debian 10 to Testing/Bullseye or
Unstable/Sid you would likely end up downgrading Buildah because the version in
OBS is more frequently updated than the one in Debian's official repositories,
due to how Debian works.
### [Fedora](https://www.fedoraproject.org)
@@ -125,7 +145,8 @@ sudo yum -y install buildah
#### [Raspberry Pi OS armhf (ex Raspbian)](https://www.raspberrypi.org/downloads/raspberry-pi-os/)
The Kubic project provides packages for Raspbian 10.
The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides
packages for Raspbian 10.
```bash
# Raspbian 10
@@ -135,6 +156,8 @@ sudo apt-get update -qq
sudo apt-get -qq -y install buildah
```
The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
#### [Raspberry Pi OS arm64 (beta)](https://downloads.raspberrypi.org/raspios_arm64/images/)
Raspberry Pi OS use the standard Debian's repositories,
@@ -160,7 +183,16 @@ sudo apt-get -y update
sudo apt-get -y install buildah
```
The [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah) provides packages for some older but supported Ubuntu versions (it should also work with direct derivatives like Pop!\_OS).
If you would prefer newer (though not as well-tested) packages,
the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/buildah)
provides packages for active Ubuntu releases 18.04 and newer (it should also work with direct derivatives like Pop!\_OS).
The packages in Kubic project repos are more frequently updated than the one in Ubuntu's official repositories, due to how Debian/Ubuntu works.
Checkout the Kubic project page for a list of supported Ubuntu version and architecture combinations.
The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/buildah/-/tree/debian/debian).
CAUTION: On Ubuntu 20.10 and newer, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo
OR the official Ubuntu repos. Mixing and matching may lead to unpredictable situations including installation conflicts.
```bash
. /etc/os-release
@@ -473,6 +505,13 @@ cat /etc/containers/policy.json
}
```
## Debug with Delve and the like
To make a source debug build without optimizations use `DEBUG=1`, like:
```
make all DEBUG=1
```
## Vendoring
Buildah uses Go Modules for vendoring purposes. If you need to update or add a vendored package into Buildah, please follow this procedure:

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"math/rand"
"os"
"strings"
"github.com/containers/buildah/util"
@@ -127,27 +126,10 @@ func resolveLocalImage(systemContext *types.SystemContext, store storage.Store,
return nil, "", nil, nil
}
// getShortNameMode looks up the `CONTAINERS_SHORT_NAME_ALIASING` environment
// variable. If it's "on", return `nil` to use the defaults from
// containers/image and the registries.conf files on the system. If it's
// "off", empty or unset, return types.ShortNameModeDisabled to turn off
// short-name aliasing by default.
//
// TODO: remove this function once we want to default to short-name aliasing.
func getShortNameMode() *types.ShortNameMode {
env := os.Getenv("CONTAINERS_SHORT_NAME_ALIASING")
if strings.ToLower(env) == "on" {
return nil // default to whatever registries.conf and c/image decide
}
mode := types.ShortNameModeDisabled
return &mode
}
func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) {
if systemContext == nil {
systemContext = &types.SystemContext{}
}
systemContext.ShortNameMode = getShortNameMode()
fromImage := options.FromImage
// If the image name includes a transport we can use it as it. Special

View File

@@ -17,6 +17,7 @@ import (
"github.com/containers/common/pkg/auth"
commonComp "github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/spf13/pflag"
@@ -366,6 +367,9 @@ func DefaultIsolation() string {
if isolation != "" {
return isolation
}
if unshare.IsRootless() {
return "rootless"
}
return buildah.OCI
}

View File

@@ -486,7 +486,7 @@ func ValidateVolumeCtrDir(ctrDir string) error {
// ValidateVolumeOpts validates a volume's options
func ValidateVolumeOpts(options []string) ([]string, error) {
var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid int
var foundRootPropagation, foundRWRO, foundLabelChange, bindType, foundExec, foundDev, foundSuid, foundChown int
finalOpts := make([]string, 0, len(options))
for _, opt := range options {
switch opt {
@@ -515,6 +515,11 @@ func ValidateVolumeOpts(options []string) ([]string, error) {
if foundLabelChange > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", "))
}
case "U":
foundChown++
if foundChown > 1 {
return nil, errors.Errorf("invalid options %q, can only specify 1 'U' option", strings.Join(options, ", "))
}
case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable":
foundRootPropagation++
if foundRootPropagation > 1 {
@@ -878,20 +883,12 @@ func NamespaceOptions(c *cobra.Command) (namespaceOptions buildah.NamespaceOptio
logrus.Debugf("setting network to disabled")
break
}
if !filepath.IsAbs(how) {
options.AddOrReplace(buildah.NamespaceOption{
Name: what,
Path: how,
})
policy = buildah.NetworkEnabled
logrus.Debugf("setting network configuration to %q", how)
break
}
}
how = strings.TrimPrefix(how, "ns:")
if _, err := os.Stat(how); err != nil {
return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace at %q", what, how)
return nil, buildah.NetworkDefault, errors.Wrapf(err, "error checking for %s namespace", what)
}
policy = buildah.NetworkEnabled
logrus.Debugf("setting %q namespace to %q", what, how)
options.AddOrReplace(buildah.NamespaceOption{
Name: what,

View File

@@ -506,8 +506,14 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
return err
}
// Get host UID and GID of the container process.
processUID, processGID, err := util.GetHostIDs(spec.Linux.UIDMappings, spec.Linux.GIDMappings, spec.Process.User.UID, spec.Process.User.GID)
if err != nil {
return err
}
// Get the list of explicitly-specified volume mounts.
volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID))
volumes, err := b.runSetupVolumeMounts(spec.Linux.MountLabel, volumeMounts, optionMounts, int(rootUID), int(rootGID), int(processUID), int(processGID))
if err != nil {
return err
}
@@ -1687,7 +1693,7 @@ func (b *Builder) cleanupTempVolumes() {
}
}
func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID int) (mounts []specs.Mount, Err error) {
func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, rootUID, rootGID, processUID, processGID int) (mounts []specs.Mount, Err error) {
// Make sure the overlay directory is clean before running
containerDir, err := b.store.ContainerDirectory(b.ContainerID)
@@ -1699,7 +1705,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
}
parseMount := func(mountType, host, container string, options []string) (specs.Mount, error) {
var foundrw, foundro, foundz, foundZ, foundO bool
var foundrw, foundro, foundz, foundZ, foundO, foundU bool
var rootProp string
for _, opt := range options {
switch opt {
@@ -1713,6 +1719,8 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
foundZ = true
case "O":
foundO = true
case "U":
foundU = true
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
rootProp = opt
}
@@ -1730,6 +1738,11 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
return specs.Mount{}, err
}
}
if foundU {
if err := chownSourceVolume(host, processUID, processGID); err != nil {
return specs.Mount{}, err
}
}
if foundO {
containerDir, err := b.store.ContainerDirectory(b.ContainerID)
if err != nil {
@@ -1746,6 +1759,14 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
b.TempVolumes[contentDir] = true
}
// If chown true, add correct ownership to the overlay temp directories.
if foundU {
if err := chownSourceVolume(contentDir, processUID, processGID); err != nil {
return specs.Mount{}, err
}
}
return overlayMount, err
}
if rootProp == "" {
@@ -1789,6 +1810,39 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
return mounts, nil
}
// chownSourceVolume changes the ownership of a volume source directory or file within the host.
func chownSourceVolume(path string, UID, GID int) error {
fi, err := os.Lstat(path)
if err != nil {
// Skip if path does not exist
if os.IsNotExist(err) {
logrus.Debugf("error returning file info of %q: %v", path, err)
return nil
}
return err
}
currentUID := int(fi.Sys().(*syscall.Stat_t).Uid)
currentGID := int(fi.Sys().(*syscall.Stat_t).Gid)
if UID != currentUID || GID != currentGID {
err := filepath.Walk(path, func(filePath string, f os.FileInfo, err error) error {
return os.Lchown(filePath, UID, GID)
})
if err != nil {
// Skip if path does not exist
if os.IsNotExist(err) {
logrus.Debugf("error changing the uid and gid of %q: %v", path, err)
return nil
}
return err
}
}
return nil
}
func setupMaskedPaths(g *generate.Generator) {
for _, mp := range []string{
"/proc/acpi",

View File

@@ -154,5 +154,5 @@ Choose one of the following:
* Complete the build operation as a privileged user.
* Install and configure fuse-overlayfs.
* Install the fuse-overlayfs package for your Linux Distribution.
* Add `mount_program = "/usr/bin/fuse-overlayfs` under `[storage.options]` in your `~/.config/containers/storage.conf` file.
* Add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in your `~/.config/containers/storage.conf` file.
---