1package ops
2
3import (
4	"context"
5	"io/ioutil"
6	"os"
7	"os/exec"
8	"path/filepath"
9
10	"github.com/containerd/containerd/mount"
11	"github.com/containerd/containerd/platforms"
12	"github.com/docker/docker/pkg/idtools"
13	"github.com/moby/buildkit/snapshot"
14	"github.com/moby/buildkit/solver/pb"
15	"github.com/moby/buildkit/util/archutil"
16	specs "github.com/opencontainers/image-spec/specs-go/v1"
17	"github.com/pkg/errors"
18	copy "github.com/tonistiigi/fsutil/copy"
19)
20
21const qemuMountName = "/dev/.buildkit_qemu_emulator"
22
23var qemuArchMap = map[string]string{
24	"arm64":   "aarch64",
25	"amd64":   "x86_64",
26	"riscv64": "riscv64",
27	"arm":     "arm",
28	"s390x":   "s390x",
29	"ppc64le": "ppc64le",
30	"386":     "i386",
31}
32
33type emulator struct {
34	path  string
35	idmap *idtools.IdentityMapping
36}
37
38func (e *emulator) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) {
39	return &staticEmulatorMount{path: e.path, idmap: e.idmap}, nil
40}
41
42type staticEmulatorMount struct {
43	path  string
44	idmap *idtools.IdentityMapping
45}
46
47func (m *staticEmulatorMount) Mount() ([]mount.Mount, func() error, error) {
48	tmpdir, err := ioutil.TempDir("", "buildkit-qemu-emulator")
49	if err != nil {
50		return nil, nil, err
51	}
52	var ret bool
53	defer func() {
54		if !ret {
55			os.RemoveAll(tmpdir)
56		}
57	}()
58
59	var uid, gid int
60	if m.idmap != nil {
61		root := m.idmap.RootPair()
62		uid = root.UID
63		gid = root.GID
64	}
65	if err := copy.Copy(context.TODO(), filepath.Dir(m.path), filepath.Base(m.path), tmpdir, qemuMountName, func(ci *copy.CopyInfo) {
66		m := 0555
67		ci.Mode = &m
68	}, copy.WithChown(uid, gid)); err != nil {
69		return nil, nil, err
70	}
71
72	ret = true
73	return []mount.Mount{{
74			Type:    "bind",
75			Source:  filepath.Join(tmpdir, qemuMountName),
76			Options: []string{"ro", "bind"},
77		}}, func() error {
78			return os.RemoveAll(tmpdir)
79		}, nil
80
81}
82func (m *staticEmulatorMount) IdentityMapping() *idtools.IdentityMapping {
83	return m.idmap
84}
85
86func getEmulator(p *pb.Platform, idmap *idtools.IdentityMapping) (*emulator, error) {
87	all := archutil.SupportedPlatforms(false)
88	m := make(map[string]struct{}, len(all))
89
90	for _, p := range all {
91		m[p] = struct{}{}
92	}
93
94	pp := platforms.Normalize(specs.Platform{
95		Architecture: p.Architecture,
96		OS:           p.OS,
97		Variant:      p.Variant,
98	})
99
100	if _, ok := m[platforms.Format(pp)]; ok {
101		return nil, nil
102	}
103
104	a, ok := qemuArchMap[pp.Architecture]
105	if !ok {
106		a = pp.Architecture
107	}
108
109	fn, err := exec.LookPath("buildkit-qemu-" + a)
110	if err != nil {
111		return nil, errors.Errorf("no emulator available for %v", pp.OS)
112	}
113
114	return &emulator{path: fn}, nil
115}
116