mirror of
https://github.com/containers/podman.git
synced 2025-12-15 03:18:45 +08:00
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:
193
vendor/github.com/containers/buildah/copier/copier.go
generated
vendored
193
vendor/github.com/containers/buildah/copier/copier.go
generated
vendored
@@ -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)
|
||||
}
|
||||
|
||||
2
vendor/github.com/containers/buildah/copier/syscall_unix.go
generated
vendored
2
vendor/github.com/containers/buildah/copier/syscall_unix.go
generated
vendored
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user