CopyFiles implements `nerdctl cp` It currently depends on the following assumptions: - linux only - tar binary exists on the system - nsenter binary exists on the system - if rootless, the container is running (aka: /proc/pid/root)
(ctx context.Context, client *containerd.Client, container containerd.Container, options types.ContainerCpOptions)
| 86 | // - nsenter binary exists on the system |
| 87 | // - if rootless, the container is running (aka: /proc/pid/root) |
| 88 | func CopyFiles(ctx context.Context, client *containerd.Client, container containerd.Container, options types.ContainerCpOptions) (err error) { |
| 89 | // We do rely on the tar binary as a shortcut - could also be replaced by archive/tar, though that would mean |
| 90 | // we need to replace nsenter calls with re-exec |
| 91 | tarBinary, isGNUTar, err := tarutil.FindTarBinary() |
| 92 | if err != nil { |
| 93 | return err |
| 94 | } |
| 95 | |
| 96 | log.G(ctx).Debugf("Detected tar binary %q (GNU=%v)", tarBinary, isGNUTar) |
| 97 | |
| 98 | // This can happen if the container being passed has been deleted since in a racy way |
| 99 | conSpec, err := container.Spec(ctx) |
| 100 | if err != nil { |
| 101 | return errors.Join(ErrContainerVanished, err) |
| 102 | } |
| 103 | |
| 104 | // Try to get a running container root |
| 105 | root, pid, err := getRoot(ctx, container) |
| 106 | // If the task is "not found" (for example, if the container stopped), we will try to mount the snapshot |
| 107 | // Any other type of error from Task() is fatal here. |
| 108 | if err != nil && !errdefs.IsNotFound(err) { |
| 109 | return errors.Join(ErrContainerVanished, err) |
| 110 | } |
| 111 | |
| 112 | log.G(ctx).Debugf("We have root %s and pid %d", root, pid) |
| 113 | |
| 114 | // If we have no root: |
| 115 | // - bail out for rootless |
| 116 | // - mount the snapshot for rootful |
| 117 | if root == "" { |
| 118 | // FIXME: Rootless does not support copying into/out of stopped/created containers as we need to nsenter into |
| 119 | // the user namespace of the pid of the running container with --preserve-credentials to preserve uid/gid |
| 120 | // mapping and copy files into the container. |
| 121 | if rootlessutil.IsRootless() { |
| 122 | return ErrRootlessCannotCp |
| 123 | } |
| 124 | |
| 125 | // See similar situation above. This may happen if we are racing against container deletion |
| 126 | var conInfo containers.Container |
| 127 | conInfo, err = container.Info(ctx) |
| 128 | if err != nil { |
| 129 | return errors.Join(ErrContainerVanished, err) |
| 130 | } |
| 131 | |
| 132 | var cleanup func() error |
| 133 | root, cleanup, err = mountSnapshotForContainer(ctx, client, conInfo, options.GOptions.Snapshotter) |
| 134 | if cleanup != nil { |
| 135 | defer func() { |
| 136 | err = errors.Join(err, cleanup()) |
| 137 | }() |
| 138 | } |
| 139 | |
| 140 | if err != nil { |
| 141 | return errors.Join(ErrFailedMountingSnapshot, err) |
| 142 | } |
| 143 | |
| 144 | log.G(ctx).Debugf("Got new root %s", root) |
| 145 | } |
no test coverage detected
searching dependent graphs…