github.com/moby/docker@v26.1.3+incompatible/daemon/containerd/image_snapshot_unix.go (about) 1 //go:build !windows 2 3 package containerd 4 5 import ( 6 "context" 7 "fmt" 8 "os" 9 "path/filepath" 10 "syscall" 11 12 "github.com/containerd/containerd/mount" 13 "github.com/containerd/containerd/snapshots" 14 "github.com/containerd/continuity/fs" 15 "github.com/containerd/continuity/sysx" 16 "github.com/docker/docker/pkg/idtools" 17 ) 18 19 const ( 20 // Values based on linux/include/uapi/linux/capability.h 21 xattrCapsSz2 = 20 22 versionOffset = 3 23 vfsCapRevision2 = 2 24 vfsCapRevision3 = 3 25 remapSuffix = "-remap" 26 ) 27 28 func (i *ImageService) remapSnapshot(ctx context.Context, snapshotter snapshots.Snapshotter, id string, parentSnapshot string) error { 29 _, err := snapshotter.Prepare(ctx, id, parentSnapshot) 30 if err != nil { 31 return err 32 } 33 mounts, err := snapshotter.Mounts(ctx, id) 34 if err != nil { 35 return err 36 } 37 38 if err := i.remapRootFS(ctx, mounts); err != nil { 39 return err 40 } 41 42 return err 43 } 44 45 func (i *ImageService) remapRootFS(ctx context.Context, mounts []mount.Mount) error { 46 return mount.WithTempMount(ctx, mounts, func(root string) error { 47 return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 48 if err != nil { 49 return err 50 } 51 52 stat := info.Sys().(*syscall.Stat_t) 53 if stat == nil { 54 return fmt.Errorf("cannot get underlying data for %s", path) 55 } 56 57 ids, err := i.idMapping.ToHost(idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)}) 58 if err != nil { 59 return err 60 } 61 62 return chownWithCaps(path, ids.UID, ids.GID) 63 }) 64 }) 65 } 66 67 func (i *ImageService) copyAndUnremapRootFS(ctx context.Context, dst, src []mount.Mount) error { 68 return mount.WithTempMount(ctx, src, func(source string) error { 69 return mount.WithTempMount(ctx, dst, func(root string) error { 70 // TODO: Update CopyDir to support remap directly 71 if err := fs.CopyDir(root, source); err != nil { 72 return fmt.Errorf("failed to copy: %w", err) 73 } 74 75 return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 76 if err != nil { 77 return err 78 } 79 80 stat := info.Sys().(*syscall.Stat_t) 81 if stat == nil { 82 return fmt.Errorf("cannot get underlying data for %s", path) 83 } 84 85 uid, gid, err := i.idMapping.ToContainer(idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)}) 86 if err != nil { 87 return err 88 } 89 90 return chownWithCaps(path, uid, gid) 91 }) 92 }) 93 }) 94 } 95 96 func (i *ImageService) unremapRootFS(ctx context.Context, mounts []mount.Mount) error { 97 return mount.WithTempMount(ctx, mounts, func(root string) error { 98 return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 99 if err != nil { 100 return err 101 } 102 103 stat := info.Sys().(*syscall.Stat_t) 104 if stat == nil { 105 return fmt.Errorf("cannot get underlying data for %s", path) 106 } 107 108 uid, gid, err := i.idMapping.ToContainer(idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)}) 109 if err != nil { 110 return err 111 } 112 113 return chownWithCaps(path, uid, gid) 114 }) 115 }) 116 } 117 118 // chownWithCaps will chown path and preserve the extended attributes. 119 // chowning a file will remove the capabilities, so we need to first get all of 120 // them, chown the file, and then set the extended attributes 121 func chownWithCaps(path string, uid int, gid int) error { 122 xattrKeys, err := sysx.LListxattr(path) 123 if err != nil { 124 return err 125 } 126 127 xattrs := make(map[string][]byte, len(xattrKeys)) 128 129 for _, xattr := range xattrKeys { 130 data, err := sysx.LGetxattr(path, xattr) 131 if err != nil { 132 return err 133 } 134 xattrs[xattr] = data 135 } 136 137 if err := os.Lchown(path, uid, gid); err != nil { 138 return err 139 } 140 141 for xattrKey, xattrValue := range xattrs { 142 length := len(xattrValue) 143 // make sure the capabilities are version 2, 144 // capabilities version 3 also store the root uid of the namespace, 145 // we don't want this when we are in userns-remap mode 146 // see: https://github.com/moby/moby/pull/41724 147 if xattrKey == "security.capability" && xattrValue[versionOffset] == vfsCapRevision3 { 148 xattrValue[versionOffset] = vfsCapRevision2 149 length = xattrCapsSz2 150 } 151 if err := sysx.LSetxattr(path, xattrKey, xattrValue[:length], 0); err != nil { 152 return err 153 } 154 } 155 156 return nil 157 }