Files
podman/pkg/emulation/binfmtmisc_linux.go
Urvashi Mohnani dd8f57a3b4 Add podman farm build command
Add podman farm build command that sends out builds to
nodes defined in the farm, builds the images on the farm
nodes, and pulls them back to the local machine to create
a manifest list.

Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
2023-10-24 12:58:39 -04:00

170 lines
4.4 KiB
Go

//go:build !remote
// +build !remote
package emulation
import (
"bufio"
"encoding/hex"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)
// registeredBinfmtMisc walks /proc/sys/fs/binfmt_misc and iterates through a
// list of known ELF header values to see if there's an emulator registered for
// them. Returns the list of emulated targets (which may be empty), or an
// error if something unexpected happened.
func registeredBinfmtMisc() ([]string, error) {
var registered []string
globalEnabled := false
err := filepath.WalkDir("/proc/sys/fs/binfmt_misc", func(path string, d fs.DirEntry, err error) error {
if filepath.Base(path) == "register" { // skip this one
return nil
}
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
info, err := d.Info()
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil // skip the directory itself
}
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
if filepath.Base(path) == "status" {
b, err := io.ReadAll(f)
if err != nil {
return err
}
status := strings.TrimSpace(string(b))
switch status {
case "disabled":
globalEnabled = false
case "enabled":
globalEnabled = true
default:
return fmt.Errorf("unrecognized binfmt_misc status value %q in %q", status, path)
}
return nil
}
offset, magic, mask, err := parseBinfmtMisc(path, f)
if err != nil {
return err
}
if offset < 0 {
return nil
}
for platform, headers := range getKnownELFPlatformHeaders() {
for _, header := range headers {
if magicMatch(header, offset, mask, magic) {
registered = append(registered, platform)
break
}
}
}
return nil
})
if !globalEnabled {
return nil, nil
}
sort.Strings(registered)
return registered, err
}
// magicMatch compares header, starting at the specified offset, masked with
// mask, against the magic value
func magicMatch(header []byte, offset int, mask, magic []byte) bool {
mismatch := 0
for i := offset; i < offset+len(magic); i++ {
if i >= len(header) {
break
}
m := magic[i-offset]
if len(mask) > i-offset {
m &= mask[i-offset]
}
if header[i] != m {
// mismatch
break
}
mismatch = i + 1
}
return mismatch >= offset+len(magic)
}
// parseBinfmtMisc parses a binfmt_misc registry entry. It returns the offset,
// magic, and mask values, or an error if there was an error parsing the data.
// If the returned offset is negative, the entry was disabled or should be
// non-fatally ignored for some other reason.
func parseBinfmtMisc(path string, r io.Reader) (int, []byte, []byte, error) {
offset := 0
magicString, maskString := "", ""
scanner := bufio.NewScanner(r)
for scanner.Scan() {
text := scanner.Text()
if strings.TrimSpace(text) == "" {
continue
}
fields := strings.Fields(text)
switch fields[0] {
case "disabled":
return -1, nil, nil, nil // we should ignore this specific one
case "enabled": // keep scanning this entry
case "interpreter": // good, but not something we need to record
case "offset":
if len(fields) != 2 {
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
}
offset64, err := strconv.ParseInt(fields[1], 10, 8)
if err != nil {
return -1, nil, nil, fmt.Errorf("invalid offset %q in %q", fields[1], path)
}
offset = int(offset64)
case "magic":
if len(fields) != 2 {
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
}
magicString = fields[1]
case "mask":
if len(fields) != 2 {
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
}
maskString = fields[1]
case "flags", "flags:":
if len(fields) != 2 {
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
}
if !strings.Contains(fields[1], "F") { // won't work in other mount namespaces, so ignore it
return -1, nil, nil, nil
}
default:
return -1, nil, nil, fmt.Errorf("unrecognized field %q in %q", fields[0], path)
}
continue
}
if magicString == "" || maskString == "" { // entry is missing some info we need here
return -1, nil, nil, nil
}
magic, err := hex.DecodeString(magicString)
if err != nil {
return -1, nil, nil, fmt.Errorf("invalid magic %q in %q", magicString, path)
}
mask, err := hex.DecodeString(maskString)
if err != nil {
return -1, nil, nil, fmt.Errorf("invalid mask %q in %q", maskString, path)
}
return offset, magic, mask, nil
}