github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/client/container_copy.go (about)

     1  package client // import "github.com/docker/docker/client"
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/url"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/docker/docker/api/types"
    15  )
    16  
    17  // ContainerStatPath returns stat information about a path inside the container filesystem.
    18  func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) {
    19  	query := url.Values{}
    20  	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
    21  
    22  	urlStr := "/containers/" + containerID + "/archive"
    23  	response, err := cli.head(ctx, urlStr, query, nil)
    24  	defer ensureReaderClosed(response)
    25  	if err != nil {
    26  		return types.ContainerPathStat{}, err
    27  	}
    28  	return getContainerPathStatFromHeader(response.header)
    29  }
    30  
    31  // CopyToContainer copies content into the container filesystem.
    32  // Note that `content` must be a Reader for a TAR archive
    33  func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options types.CopyToContainerOptions) error {
    34  	query := url.Values{}
    35  	query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
    36  	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
    37  	if !options.AllowOverwriteDirWithFile {
    38  		query.Set("noOverwriteDirNonDir", "true")
    39  	}
    40  
    41  	if options.CopyUIDGID {
    42  		query.Set("copyUIDGID", "true")
    43  	}
    44  
    45  	apiPath := "/containers/" + containerID + "/archive"
    46  
    47  	response, err := cli.putRaw(ctx, apiPath, query, content, nil)
    48  	defer ensureReaderClosed(response)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	return nil
    54  }
    55  
    56  // CopyFromContainer gets the content from the container and returns it as a Reader
    57  // for a TAR archive to manipulate it in the host. It's up to the caller to close the reader.
    58  func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    59  	query := make(url.Values, 1)
    60  	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
    61  
    62  	apiPath := "/containers/" + containerID + "/archive"
    63  	response, err := cli.get(ctx, apiPath, query, nil)
    64  	if err != nil {
    65  		return nil, types.ContainerPathStat{}, err
    66  	}
    67  
    68  	// In order to get the copy behavior right, we need to know information
    69  	// about both the source and the destination. The response headers include
    70  	// stat info about the source that we can use in deciding exactly how to
    71  	// copy it locally. Along with the stat info about the local destination,
    72  	// we have everything we need to handle the multiple possibilities there
    73  	// can be when copying a file/dir from one location to another file/dir.
    74  	stat, err := getContainerPathStatFromHeader(response.header)
    75  	if err != nil {
    76  		return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
    77  	}
    78  	return response.body, stat, err
    79  }
    80  
    81  func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
    82  	var stat types.ContainerPathStat
    83  
    84  	encodedStat := header.Get("X-Docker-Container-Path-Stat")
    85  	statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
    86  
    87  	err := json.NewDecoder(statDecoder).Decode(&stat)
    88  	if err != nil {
    89  		err = fmt.Errorf("unable to decode container path stat header: %s", err)
    90  	}
    91  
    92  	return stat, err
    93  }