Skip to content

Commit

Permalink
cp cmd
Browse files Browse the repository at this point in the history
Signed-off-by: Fahed DORGAA <fahed_dorgaa@carrefour.com>

fixes

Signed-off-by: fahed dorgaa <fahed.dorgaa@gmail.com>

fixes

Signed-off-by: fahed dorgaa <fahed.dorgaa@gmail.com>

fixes

Signed-off-by: fahed dorgaa <fahed.dorgaa@gmail.com>

fixes

Signed-off-by: fahed dorgaa <fahed.dorgaa@gmail.com>
  • Loading branch information
Fahed DORGAA authored and fahedouch committed Dec 20, 2021
1 parent a6992ab commit e93a6ff
Show file tree
Hide file tree
Showing 3 changed files with 422 additions and 1 deletion.
229 changes: 229 additions & 0 deletions cmd/nerdctl/cp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"archive/tar"
"context"
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"

"github.com/docker/docker/pkg/system"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

type cpConfig struct {
container string
followLink bool
sourcePath string
destPath string
}

var errFileSpecDoesntMatchFormat = errors.New("filespec must match the canonical format: [container:]file/path")

func newCpCommand() *cobra.Command {

shortHelp := "Copy files/folders between a container and the local filesystem"
longHelp := shortHelp + "\nNOTE: Use '-' as the source to read a tar archive from stdin\n and extract it to a directory destination in a container. \nUse '-' as the destination to stream a tar archive of a\ncontainer source to stdout."

var cpCommand = &cobra.Command{
Use: "cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-",
Args: cobra.MinimumNArgs(2),
Short: shortHelp,
Long: longHelp,
RunE: cpAction,
//ValidArgsFunction: cpShellComplete,
SilenceUsage: true,
SilenceErrors: true,
}

cpCommand.Flags().BoolP("follow-link", "L", false, "Always follow symbol link in SRC_PATH")
return cpCommand
}

func cpAction(cmd *cobra.Command, args []string) error {
srcSpec, err := splitCpArg(args[0])
if err != nil {
return err
}

destSpec, err := splitCpArg(args[0])
if err != nil {
return err
}

flagL, err := cmd.Flags().GetBool("follow-link")
if err != nil {
return err
}

copyConfig := cpConfig{
followLink: flagL,
sourcePath: srcSpec.Path,
destPath: destSpec.Path,
}

if len(srcSpec.Container) != 0 && len(destSpec.Container) != 0 {
return fmt.Errorf("one of src or dest must be a local file specification")
}
if len(srcSpec.Path) == 0 || len(destSpec.Path) == 0 {
return errors.New("filepath can not be empty")
}

_, ctx, cancel, err := newClient(cmd)
if err != nil {
return err
}
defer cancel()

if len(srcSpec.Container) != 0 {
copyConfig.container = srcSpec.Container
return copyFromContainer(ctx, cmd, copyConfig)
}
if len(destSpec.Container) != 0 {
copyConfig.container = destSpec.Container
return nil
}
return fmt.Errorf("one of src or dest must be a remote file specification")

}

func splitCpArg(arg string) (fileSpec, error) {
i := strings.Index(arg, ":")

// filespec starting with a semicolon is invalid
if i == 0 {
return fileSpec{}, errFileSpecDoesntMatchFormat
}

if system.IsAbs(arg) {
// Explicit local absolute path, e.g., `C:\foo` or `/foo`.
return fileSpec{
Path: arg,
}, nil
}

parts := strings.SplitN(arg, ":", 2)

if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
// Either there's no `:` in the arg
// OR it's an explicit local relative path like `./file:name.txt`.
return fileSpec{
Path: arg,
}, nil
}

return fileSpec{
Container: parts[0],
Path: parts[1],
}, nil
}

type fileSpec struct {
Container string
Path string
}

func copyFromContainer(ctx context.Context, cmd *cobra.Command, copyConfig cpConfig) (err error) {
dstPath := copyConfig.destPath
srcPath := copyConfig.sourcePath

if dstPath != "-" {
if dstPath, err = filepath.Abs(dstPath); err != nil {
return err
}
}

dir := filepath.Dir(filepath.Clean(dstPath))
if dir != "" && dir != "." {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return fmt.Errorf("invalid output path: directory %q does not exist", dir)
}
}

reader := newTarPipe(src, o)

tarReader := tar.NewReader(reader)
for {
header, err := tarReader.Next()
if err != nil {
if err != io.EOF {
return err
}
break
}

if copyConfig.followLink {
if mode&os.ModeSymlink != 0 {
linkTarget := header.FileInfo().Linkname()
if !filepath.IsAbs(linkTarget) {
// Join with the parent directory.
srcDir, _ := SplitPathDirEntry(srcPath)
linkTarget = filepath.Join(srcDir, linkTarget)
}

if isCurrentDir(srcDir) &&
!isCurrentDir(linkTarget) {
linkTarget += string(filepath.Separator) + "."
}

if hasTrailingPathSeparator(srcDir, os.PathSeparator) &&
!hasTrailingPathSeparator(linkTarget, os.PathSeparator) {
linkTarget += string(filepath.Separator)
}

srcPath = linkTarget
}
}

// srcPath Cleaning
trimmedPath := strings.TrimLeft(srcPath, `/\`) // tar strips the leading '/' and '\' if it's there, so we will too
cleanedPath := filepath.Clean(trimmed)
prefix := stripPathShortcuts(cleaned.String())

// basic file information
mode := header.FileInfo().Mode()

// header.Name is a name of the REMOTE file, so we need to create
// a remotePath that will goes through appropriate cleaning process
destFileName := path.Join(dstPath, filepath.Clean(header.Name[len(prefix):]))

if !isRelative(dstPath, destFileName) {
logrus.Warnf("skipping %q: file is outside target destination", destFileName)
continue
}

// docker does not manage the creation of non-existent path
if err := os.MkdirAll(destFileName.Dir().String(), 0755); err != nil {
return err
}
if header.FileInfo().IsDir() {
if err := os.MkdirAll(destFileName.String(), 0755); err != nil {
return err
}
continue
}

}

}
119 changes: 118 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/containerd/nerdctl

go 1.16
go 1.17

require (
github.com/Microsoft/go-winio v0.5.1
Expand Down Expand Up @@ -52,5 +52,122 @@ require (
gotest.tools/v3 v3.0.3
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/hcsshim v0.9.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/btcsuite/btcd v0.21.0-beta // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/cilium/ebpf v0.6.2 // indirect
github.com/compose-spec/godotenv v1.1.0 // indirect
github.com/containerd/fifo v1.0.0 // indirect
github.com/containerd/ttrpc v1.1.0 // indirect
github.com/containers/ocicrypt v1.1.1 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/gogo/googleapis v1.4.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/ipfs/bbloom v0.0.4 // indirect
github.com/ipfs/go-block-format v0.0.3 // indirect
github.com/ipfs/go-blockservice v0.1.7 // indirect
github.com/ipfs/go-datastore v0.4.5 // indirect
github.com/ipfs/go-ipfs-blockstore v0.1.6 // indirect
github.com/ipfs/go-ipfs-cmds v0.3.0 // indirect
github.com/ipfs/go-ipfs-ds-help v0.1.1 // indirect
github.com/ipfs/go-ipfs-exchange-interface v0.0.1 // indirect
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
github.com/ipfs/go-ipld-cbor v0.0.5 // indirect
github.com/ipfs/go-ipld-format v0.2.0 // indirect
github.com/ipfs/go-ipld-legacy v0.1.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.1.3 // indirect
github.com/ipfs/go-merkledag v0.4.0 // indirect
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
github.com/ipfs/go-path v0.1.1 // indirect
github.com/ipfs/go-unixfs v0.2.4 // indirect
github.com/ipfs/go-verifcid v0.0.1 // indirect
github.com/ipld/go-codec-dagpb v1.3.0 // indirect
github.com/ipld/go-ipld-prime v0.11.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/libp2p/go-libp2p-core v0.8.5 // indirect
github.com/libp2p/go-openssl v0.0.7 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/mountinfo v0.5.0 // indirect
github.com/moby/sys/signal v0.6.0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/multiformats/go-multiaddr-net v0.2.0 // indirect
github.com/multiformats/go-multibase v0.0.3 // indirect
github.com/multiformats/go-multihash v0.0.15 // indirect
github.com/multiformats/go-varint v0.0.6 // indirect
github.com/opencontainers/runc v1.0.2 // indirect
github.com/opencontainers/selinux v1.8.2 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/urfave/cli v1.22.4 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.24.0 // indirect
go.opentelemetry.io/otel v1.0.1 // indirect
go.opentelemetry.io/otel/trace v1.0.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/net v0.0.0-20211105192438-b53810dc28af // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/grpc v1.42.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
)

// Temporary fork for avoiding importing patent-protected code: https://github.com/hashicorp/golang-lru/issues/73
replace github.com/hashicorp/golang-lru => github.com/ktock/golang-lru v0.5.5-0.20211029085301-ec551be6f75c
Loading

0 comments on commit e93a6ff

Please sign in to comment.