container_opts_unix.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // +build !windows
  2. /*
  3. Copyright The containerd Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package containerd
  15. import (
  16. "context"
  17. "fmt"
  18. "os"
  19. "path/filepath"
  20. "syscall"
  21. "github.com/containerd/containerd/containers"
  22. "github.com/containerd/containerd/content"
  23. "github.com/containerd/containerd/errdefs"
  24. "github.com/containerd/containerd/images"
  25. "github.com/containerd/containerd/mount"
  26. "github.com/containerd/containerd/platforms"
  27. "github.com/gogo/protobuf/proto"
  28. protobuf "github.com/gogo/protobuf/types"
  29. "github.com/opencontainers/image-spec/identity"
  30. "github.com/opencontainers/image-spec/specs-go/v1"
  31. "github.com/pkg/errors"
  32. )
  33. // WithCheckpoint allows a container to be created from the checkpointed information
  34. // provided by the descriptor. The image, snapshot, and runtime specifications are
  35. // restored on the container
  36. func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts {
  37. // set image and rw, and spec
  38. return func(ctx context.Context, client *Client, c *containers.Container) error {
  39. var (
  40. desc = im.Target()
  41. store = client.ContentStore()
  42. )
  43. index, err := decodeIndex(ctx, store, desc)
  44. if err != nil {
  45. return err
  46. }
  47. var rw *v1.Descriptor
  48. for _, m := range index.Manifests {
  49. switch m.MediaType {
  50. case v1.MediaTypeImageLayer:
  51. fk := m
  52. rw = &fk
  53. case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList:
  54. config, err := images.Config(ctx, store, m, platforms.Default())
  55. if err != nil {
  56. return errors.Wrap(err, "unable to resolve image config")
  57. }
  58. diffIDs, err := images.RootFS(ctx, store, config)
  59. if err != nil {
  60. return errors.Wrap(err, "unable to get rootfs")
  61. }
  62. setSnapshotterIfEmpty(c)
  63. if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, snapshotKey, identity.ChainID(diffIDs).String()); err != nil {
  64. if !errdefs.IsAlreadyExists(err) {
  65. return err
  66. }
  67. }
  68. c.Image = index.Annotations["image.name"]
  69. case images.MediaTypeContainerd1CheckpointConfig:
  70. data, err := content.ReadBlob(ctx, store, m)
  71. if err != nil {
  72. return errors.Wrap(err, "unable to read checkpoint config")
  73. }
  74. var any protobuf.Any
  75. if err := proto.Unmarshal(data, &any); err != nil {
  76. return err
  77. }
  78. c.Spec = &any
  79. }
  80. }
  81. if rw != nil {
  82. // apply the rw snapshot to the new rw layer
  83. mounts, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, snapshotKey)
  84. if err != nil {
  85. return errors.Wrapf(err, "unable to get mounts for %s", snapshotKey)
  86. }
  87. if _, err := client.DiffService().Apply(ctx, *rw, mounts); err != nil {
  88. return errors.Wrap(err, "unable to apply rw diff")
  89. }
  90. }
  91. c.SnapshotKey = snapshotKey
  92. return nil
  93. }
  94. }
  95. // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
  96. // filesystem to be used by a container with user namespaces
  97. func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
  98. return withRemappedSnapshotBase(id, i, uid, gid, false)
  99. }
  100. // WithRemappedSnapshotView is similar to WithRemappedSnapshot but rootfs is mounted as read-only.
  101. func WithRemappedSnapshotView(id string, i Image, uid, gid uint32) NewContainerOpts {
  102. return withRemappedSnapshotBase(id, i, uid, gid, true)
  103. }
  104. func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts {
  105. return func(ctx context.Context, client *Client, c *containers.Container) error {
  106. diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default())
  107. if err != nil {
  108. return err
  109. }
  110. setSnapshotterIfEmpty(c)
  111. var (
  112. snapshotter = client.SnapshotService(c.Snapshotter)
  113. parent = identity.ChainID(diffIDs).String()
  114. usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
  115. )
  116. if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
  117. if _, err := snapshotter.Prepare(ctx, id, usernsID); err == nil {
  118. c.SnapshotKey = id
  119. c.Image = i.Name()
  120. return nil
  121. } else if !errdefs.IsNotFound(err) {
  122. return err
  123. }
  124. }
  125. mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
  126. if err != nil {
  127. return err
  128. }
  129. if err := remapRootFS(ctx, mounts, uid, gid); err != nil {
  130. snapshotter.Remove(ctx, usernsID)
  131. return err
  132. }
  133. if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
  134. return err
  135. }
  136. if readonly {
  137. _, err = snapshotter.View(ctx, id, usernsID)
  138. } else {
  139. _, err = snapshotter.Prepare(ctx, id, usernsID)
  140. }
  141. if err != nil {
  142. return err
  143. }
  144. c.SnapshotKey = id
  145. c.Image = i.Name()
  146. return nil
  147. }
  148. }
  149. func remapRootFS(ctx context.Context, mounts []mount.Mount, uid, gid uint32) error {
  150. return mount.WithTempMount(ctx, mounts, func(root string) error {
  151. return filepath.Walk(root, incrementFS(root, uid, gid))
  152. })
  153. }
  154. func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc {
  155. return func(path string, info os.FileInfo, err error) error {
  156. if err != nil {
  157. return err
  158. }
  159. var (
  160. stat = info.Sys().(*syscall.Stat_t)
  161. u, g = int(stat.Uid + uidInc), int(stat.Gid + gidInc)
  162. )
  163. // be sure the lchown the path as to not de-reference the symlink to a host file
  164. return os.Lchown(path, u, g)
  165. }
  166. }