github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_cp_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"github.com/containerd/nerdctl/pkg/api/types"
    23  	"github.com/containerd/nerdctl/pkg/clientutil"
    24  	"github.com/containerd/nerdctl/pkg/cmd/container"
    25  	"github.com/containerd/nerdctl/pkg/rootlessutil"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  func newCpCommand() *cobra.Command {
    30  	shortHelp := "Copy files/folders between a running container and the local filesystem."
    31  
    32  	longHelp := shortHelp + `
    33  This command requires 'tar' to be installed on the host (not in the container).
    34  Using GNU tar is recommended.
    35  The path of the 'tar' binary can be specified with an environment variable '$TAR'.
    36  
    37  WARNING: 'nerdctl cp' is designed only for use with trusted, cooperating containers.
    38  Using 'nerdctl cp' with untrusted or malicious containers is unsupported and may not provide protection against unexpected behavior.
    39  `
    40  
    41  	usage := `cp [flags] CONTAINER:SRC_PATH DEST_PATH|-
    42    nerdctl cp [flags] SRC_PATH|- CONTAINER:DEST_PATH`
    43  	var cpCommand = &cobra.Command{
    44  		Use:               usage,
    45  		Args:              IsExactArgs(2),
    46  		Short:             shortHelp,
    47  		Long:              longHelp,
    48  		RunE:              cpAction,
    49  		ValidArgsFunction: cpShellComplete,
    50  		SilenceUsage:      true,
    51  		SilenceErrors:     true,
    52  	}
    53  
    54  	cpCommand.Flags().BoolP("follow-link", "L", false, "Always follow symbolic link in SRC_PATH.")
    55  
    56  	return cpCommand
    57  }
    58  
    59  func cpAction(cmd *cobra.Command, args []string) error {
    60  	options, err := processCpOptions(cmd, args)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	if rootlessutil.IsRootless() {
    65  		options.GOptions.Address, err = rootlessutil.RootlessContainredSockAddress()
    66  		if err != nil {
    67  			return err
    68  		}
    69  	}
    70  	client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	defer cancel()
    75  
    76  	return container.Cp(ctx, client, options)
    77  }
    78  
    79  func processCpOptions(cmd *cobra.Command, args []string) (types.ContainerCpOptions, error) {
    80  	globalOptions, err := processRootCmdFlags(cmd)
    81  	if err != nil {
    82  		return types.ContainerCpOptions{}, err
    83  	}
    84  	flagL, err := cmd.Flags().GetBool("follow-link")
    85  	if err != nil {
    86  		return types.ContainerCpOptions{}, err
    87  	}
    88  
    89  	srcSpec, err := parseCpFileSpec(args[0])
    90  	if err != nil {
    91  		return types.ContainerCpOptions{}, err
    92  	}
    93  
    94  	destSpec, err := parseCpFileSpec(args[1])
    95  	if err != nil {
    96  		return types.ContainerCpOptions{}, err
    97  	}
    98  
    99  	if (srcSpec.Container != nil && destSpec.Container != nil) || (len(srcSpec.Path) == 0 && len(destSpec.Path) == 0) {
   100  		return types.ContainerCpOptions{}, fmt.Errorf("one of src or dest must be a local file specification")
   101  	}
   102  	if srcSpec.Container == nil && destSpec.Container == nil {
   103  		return types.ContainerCpOptions{}, fmt.Errorf("one of src or dest must be a container file specification")
   104  	}
   105  	if srcSpec.Path == "-" {
   106  		return types.ContainerCpOptions{}, fmt.Errorf("support for reading a tar archive from stdin is not implemented yet")
   107  	}
   108  	if destSpec.Path == "-" {
   109  		return types.ContainerCpOptions{}, fmt.Errorf("support for writing a tar archive to stdout is not implemented yet")
   110  	}
   111  
   112  	container2host := srcSpec.Container != nil
   113  	var container string
   114  	if container2host {
   115  		container = *srcSpec.Container
   116  	} else {
   117  		container = *destSpec.Container
   118  	}
   119  	return types.ContainerCpOptions{
   120  		GOptions:       globalOptions,
   121  		Container2Host: container2host,
   122  		ContainerReq:   container,
   123  		DestPath:       destSpec.Path,
   124  		SrcPath:        srcSpec.Path,
   125  		FollowSymLink:  flagL,
   126  	}, nil
   127  }