mirror of
				https://github.com/containers/podman.git
				synced 2025-11-04 08:56:05 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			169 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//go: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
 | 
						|
}
 |