Skip to content

Commit

Permalink
Merge pull request #5732 from nalind/binfmt-container
Browse files Browse the repository at this point in the history
In a container, try to register binfmt_misc
  • Loading branch information
openshift-merge-bot[bot] authored Sep 13, 2024
2 parents bd12ae1 + baf91f2 commit b4d5c2f
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ LIBSECCOMP_COMMIT := release-2.3

EXTRA_LDFLAGS ?=
BUILDAH_LDFLAGS := $(GO_LDFLAGS) '-X main.GitCommit=$(GIT_COMMIT) -X main.buildInfo=$(SOURCE_DATE_EPOCH) -X main.cniVersion=$(CNI_COMMIT) $(EXTRA_LDFLAGS)'
SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/config/*.go internal/mkcw/*.go internal/mkcw/types/*.go internal/parse/*.go internal/sbom/*.go internal/source/*.go internal/tmpdir/*.go internal/*.go internal/util/*.go internal/volumes/*.go manifests/*.go pkg/blobcache/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/jail/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go pkg/volumes/*.go util/*.go
SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/config/*.go internal/mkcw/*.go internal/mkcw/types/*.go internal/parse/*.go internal/sbom/*.go internal/source/*.go internal/tmpdir/*.go internal/*.go internal/util/*.go internal/volumes/*.go manifests/*.go pkg/binfmt/*.go pkg/blobcache/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/jail/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go pkg/volumes/*.go util/*.go

LINTFLAGS ?=

Expand Down
83 changes: 83 additions & 0 deletions pkg/binfmt/binfmt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//go:build linux

package binfmt

import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"

"github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

// MaybeRegister() calls Register() if the current context is a rootless one,
// or if the "container" environment variable suggests that we're in a
// container.
func MaybeRegister(configurationSearchDirectories []string) error {
if unshare.IsRootless() || os.Getenv("container") != "" { // we _also_ own our own mount namespace
return Register(configurationSearchDirectories)
}
return nil
}

// Register() registers binfmt.d emulators described by configuration files in
// the passed-in slice of directories, or in the union of /etc/binfmt.d,
// /run/binfmt.d, and /usr/lib/binfmt.d if the slice has no items. If any
// emulators are configured, it will attempt to mount a binfmt_misc filesystem
// in the current mount namespace first, ignoring only EPERM and EACCES errors.
func Register(configurationSearchDirectories []string) error {
if len(configurationSearchDirectories) == 0 {
configurationSearchDirectories = []string{"/etc/binfmt.d", "/run/binfmt.d", "/usr/lib/binfmt.d"}
}
mounted := false
for _, searchDir := range configurationSearchDirectories {
globs, err := filepath.Glob(filepath.Join(searchDir, "*.conf"))
if err != nil {
return fmt.Errorf("looking for binfmt.d configuration in %q: %w", searchDir, err)
}
for _, conf := range globs {
f, err := os.Open(conf)
if err != nil {
return fmt.Errorf("reading binfmt.d configuration: %w", err)
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) == 0 || line[0] == ';' || line[0] == '#' {
continue
}
if !mounted {
if err = unix.Mount("none", "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, ""); err != nil {
if errors.Is(err, syscall.EPERM) || errors.Is(err, syscall.EACCES) {
// well, we tried. no need to make a stink about it
return nil
}
return fmt.Errorf("mounting binfmt_misc: %w", err)
}
mounted = true
}
reg, err := os.Create("/proc/sys/fs/binfmt_misc/register")
if err != nil {
return fmt.Errorf("registering(open): %w", err)
}
if _, err = fmt.Fprintf(reg, "%s\n", line); err != nil {
return fmt.Errorf("registering(write): %w", err)
}
logrus.Tracef("registered binfmt %q", line)
if err = reg.Close(); err != nil {
return fmt.Errorf("registering(close): %w", err)
}
}
if err := f.Close(); err != nil {
return fmt.Errorf("reading binfmt.d configuration: %w", err)
}
}
}
return nil
}
15 changes: 15 additions & 0 deletions pkg/binfmt/binfmt_unsupported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !linux

package binfmt

import "syscall"

// MaybeRegister() returns no error.
func MaybeRegister(configurationSearchDirectories []string) error {
return nil
}

// Register() returns an error.
func Register(configurationSearchDirectories []string) error {
return syscall.ENOSYS
}
38 changes: 30 additions & 8 deletions run_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"syscall"

"github.com/containers/buildah/bind"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/containers/buildah/internal"
"github.com/containers/buildah/internal/tmpdir"
"github.com/containers/buildah/internal/volumes"
"github.com/containers/buildah/pkg/binfmt"
"github.com/containers/buildah/pkg/overlay"
"github.com/containers/buildah/pkg/parse"
butil "github.com/containers/buildah/pkg/util"
Expand Down Expand Up @@ -49,14 +51,19 @@ import (
"tags.cncf.io/container-device-interface/pkg/parser"
)

// We dont want to remove destinations with /etc, /dev, /sys,
// /proc as rootfs already contains these files and unionfs
// will create a `whiteout` i.e `.wh` files on removal of
// overlapping files from these directories. everything other
// than these will be cleaned up
var nonCleanablePrefixes = []string{
"/etc", "/dev", "/sys", "/proc",
}
var (
// We dont want to remove destinations with /etc, /dev, /sys,
// /proc as rootfs already contains these files and unionfs
// will create a `whiteout` i.e `.wh` files on removal of
// overlapping files from these directories. everything other
// than these will be cleaned up
nonCleanablePrefixes = []string{
"/etc", "/dev", "/sys", "/proc",
}
// binfmtRegistered makes sure we only try to register binfmt_misc
// interpreters once, the first time we handle a RUN instruction.
binfmtRegistered sync.Once
)

func setChildProcess() error {
if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(1), 0, 0, 0); err != nil {
Expand Down Expand Up @@ -158,6 +165,21 @@ func separateDevicesFromRuntimeSpec(g *generate.Generator) define.ContainerDevic

// Run runs the specified command in the container's root filesystem.
func (b *Builder) Run(command []string, options RunOptions) error {
if os.Getenv("container") != "" {
os, arch, variant, err := parse.Platform("")
if err != nil {
return fmt.Errorf("reading the current default platform")
}
platform := b.OCIv1.Platform
if os != platform.OS || arch != platform.Architecture || variant != platform.Variant {
binfmtRegistered.Do(func() {
if err := binfmt.Register(nil); err != nil {
logrus.Warnf("registering binfmt_misc interpreters: %v", err)
}
})
}
}

p, err := os.MkdirTemp(tmpdir.GetTempDir(), define.Package)
if err != nil {
return err
Expand Down

2 comments on commit b4d5c2f

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

@packit-as-a-service
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

podman-next COPR build failed. @containers/packit-build please check.

Please sign in to comment.