//go:build !windows package containerdexecutor import ( "context" "os" "path/filepath" "runtime" "slices" ctd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/mount" containerdoci "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/continuity/fs" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/executor/oci" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/network" rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv" "github.com/moby/sys/user" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) func getUserSpec(user, rootfsPath string) (specs.User, error) { var err error var uid, gid uint32 var sgids []uint32 if rootfsPath != "" { uid, gid, sgids, err = oci.GetUser(rootfsPath, user) } else { uid, gid, err = oci.ParseUIDGID(user) } if err != nil { return specs.User{}, errors.WithStack(err) } return specs.User{ UID: uid, GID: gid, AdditionalGids: sgids, }, nil } func (w *containerdExecutor) prepareExecutionEnv(ctx context.Context, rootMount executor.Mount, mounts []executor.Mount, meta executor.Meta, details *containerState, netMode pb.NetMode) (string, string, func(), error) { var releasers []func() releaseAll := func() { for _, release := range slices.Backward(releasers) { release() } } stateDirRoot, err := os.OpenRoot(w.root) if err != nil { releaseAll() return "", "", nil, err } defer stateDirRoot.Close() resolvConfName, err := oci.GetResolvConf(ctx, stateDirRoot, nil, w.dnsConfig, netMode) if err != nil { releaseAll() return "", "", nil, err } resolvConf := filepath.Join(w.root, resolvConfName) hostsName, clean, err := oci.GetHostsFile(ctx, stateDirRoot, meta.ExtraHosts, nil, meta.Hostname) if err != nil { releaseAll() return "", "", nil, err } hostsFile := filepath.Join(w.root, hostsName) if clean != nil { releasers = append(releasers, clean) } mountable, err := rootMount.Src.Mount(ctx, false) if err != nil { releaseAll() return "", "", nil, err } rootMounts, release, err := mountable.Mount() if err != nil { releaseAll() return "", "", nil, err } details.rootMounts = rootMounts if release != nil { releasers = append(releasers, func() { if err := release(); err != nil { bklog.G(ctx).WithError(err).Error("failed to release root mount") } }) } lm := snapshot.LocalMounterWithMounts(rootMounts) rootfsPath, err := lm.Mount() if err != nil { releaseAll() return "", "", nil, err } details.rootfsPath = rootfsPath releasers = append(releasers, func() { if err := lm.Unmount(); err != nil { bklog.G(ctx).WithError(err).Error("failed to unmount rootfs") } }) releasers = append(releasers, executor.MountStubsCleaner(ctx, details.rootfsPath, mounts, meta.RemoveMountStubsRecursive)) return resolvConf, hostsFile, releaseAll, nil } func (w *containerdExecutor) ensureCWD(details *containerState, meta executor.Meta) error { newp, err := fs.RootPath(details.rootfsPath, meta.Cwd) if err != nil { return errors.Wrapf(err, "working dir %s points to invalid target", newp) } uid, gid, _, err := oci.GetUser(details.rootfsPath, meta.User) if err != nil { return err } if _, err := os.Stat(newp); err != nil { if err := user.MkdirAllAndChown(newp, 0755, int(uid), int(gid)); err != nil { return errors.Wrapf(err, "failed to create working directory %s", newp) } } return nil } func (w *containerdExecutor) createOCISpec(ctx context.Context, id, resolvConf, hostsFile string, namespace network.Namespace, mounts []executor.Mount, meta executor.Meta, details *containerState) (*specs.Spec, func(), error) { var releasers []func() releaseAll := func() { for _, release := range slices.Backward(releasers) { release() } } uid, gid, sgids, err := oci.GetUser(details.rootfsPath, meta.User) if err != nil { releaseAll() return nil, nil, err } opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)} if meta.ReadonlyRootFS { opts = append(opts, containerdoci.WithRootFSReadonly()) } processMode := oci.ProcessSandbox // FIXME(AkihiroSuda) spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.cgroupParent, processMode, nil, w.apparmorProfile, w.selinux, w.traceSocket, w.cdiManager, opts...) if err != nil { releaseAll() return nil, nil, err } releasers = append(releasers, cleanup) spec.Process.Terminal = meta.Tty if w.rootless { if err := rootlessspecconv.ToRootless(spec); err != nil { releaseAll() return nil, nil, err } } return spec, releaseAll, nil } func (d *containerState) getTaskOpts() ([]ctd.NewTaskOpts, error) { rootfs := ctd.WithRootFS([]mount.Mount{{ Source: d.rootfsPath, Type: "bind", Options: []string{"rbind"}, }}) if runtime.GOOS == "freebsd" { rootfs = ctd.WithRootFS([]mount.Mount{{ Source: d.rootfsPath, Type: "nullfs", Options: []string{}, }}) } return []ctd.NewTaskOpts{rootfs}, nil } func setArgs(spec *specs.Process, args []string) { spec.Args = args }