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