Skip to content

Commit

Permalink
In a container, try to register binfmt_misc
Browse files Browse the repository at this point in the history
If we're being started up in a container, default to attempting to
register any emulators for which we find configurations of the type
included in Fedora's qemu-user-static packages.

Use a hidden global --register-binfmt flag to control this, and set its
default based on whether or not $container is set to "oci".

Signed-off-by: Nalin Dahyabhai <[email protected]>
  • Loading branch information
nalind committed Sep 12, 2024
1 parent 18d09f0 commit 4aedfc1
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
11 changes: 11 additions & 0 deletions cmd/buildah/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/containers/buildah"
"github.com/containers/buildah/define"
"github.com/containers/buildah/pkg/binfmt"
"github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/common/pkg/config"
Expand Down Expand Up @@ -40,6 +41,7 @@ type globalFlags struct {
MemoryProfile string
UserShortNameAliasConfPath string
CgroupManager string
registerBinfmt bool
}

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -118,6 +120,10 @@ func init() {
if err := rootCmd.PersistentFlags().MarkHidden("memory-profile"); err != nil {
logrus.Fatalf("unable to mark memory-profile flag as hidden: %v", err)
}
rootCmd.PersistentFlags().BoolVar(&globalFlagResults.registerBinfmt, "register-binfmt", os.Getenv("container") != "", "attempt to register binfmt_misc interpreters")
if err := rootCmd.PersistentFlags().MarkHidden("register-binfmt"); err != nil {
logrus.Fatalf("unable to mark register-binfmt flag as hidden: %v", err)
}
}

func initConfig() {
Expand Down Expand Up @@ -147,6 +153,11 @@ func before(cmd *cobra.Command) error {
}
debugCapabilities()
unshare.MaybeReexecUsingUserNamespace(false)
if globalFlagResults.registerBinfmt {
if err := binfmt.Register(nil); err != nil {
logrus.Debugf("registering binfmt_misc interpreters: %v", err)
}
}
if globalFlagResults.CPUProfile != "" {
globalFlagResults.cpuProfileFile, err = os.Create(globalFlagResults.CPUProfile)
if err != nil {
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
}

0 comments on commit 4aedfc1

Please sign in to comment.