podman

Форк
0
/
binfmtmisc_linux.go 
168 строк · 4.4 Кб
1
//go:build !remote
2

3
package emulation
4

5
import (
6
	"bufio"
7
	"encoding/hex"
8
	"errors"
9
	"fmt"
10
	"io"
11
	"io/fs"
12
	"os"
13
	"path/filepath"
14
	"sort"
15
	"strconv"
16
	"strings"
17
)
18

19
// registeredBinfmtMisc walks /proc/sys/fs/binfmt_misc and iterates through a
20
// list of known ELF header values to see if there's an emulator registered for
21
// them.  Returns the list of emulated targets (which may be empty), or an
22
// error if something unexpected happened.
23
func registeredBinfmtMisc() ([]string, error) {
24
	var registered []string
25
	globalEnabled := false
26
	err := filepath.WalkDir("/proc/sys/fs/binfmt_misc", func(path string, d fs.DirEntry, err error) error {
27
		if filepath.Base(path) == "register" { // skip this one
28
			return nil
29
		}
30
		if err != nil && !errors.Is(err, os.ErrNotExist) {
31
			return err
32
		}
33
		info, err := d.Info()
34
		if err != nil {
35
			return err
36
		}
37
		if !info.Mode().IsRegular() {
38
			return nil // skip the directory itself
39
		}
40
		f, err := os.Open(path)
41
		if err != nil {
42
			return err
43
		}
44
		defer f.Close()
45
		if filepath.Base(path) == "status" {
46
			b, err := io.ReadAll(f)
47
			if err != nil {
48
				return err
49
			}
50
			status := strings.TrimSpace(string(b))
51
			switch status {
52
			case "disabled":
53
				globalEnabled = false
54
			case "enabled":
55
				globalEnabled = true
56
			default:
57
				return fmt.Errorf("unrecognized binfmt_misc status value %q in %q", status, path)
58
			}
59
			return nil
60
		}
61
		offset, magic, mask, err := parseBinfmtMisc(path, f)
62
		if err != nil {
63
			return err
64
		}
65
		if offset < 0 {
66
			return nil
67
		}
68
		for platform, headers := range getKnownELFPlatformHeaders() {
69
			for _, header := range headers {
70
				if magicMatch(header, offset, mask, magic) {
71
					registered = append(registered, platform)
72
					break
73
				}
74
			}
75
		}
76
		return nil
77
	})
78
	if !globalEnabled {
79
		return nil, nil
80
	}
81
	sort.Strings(registered)
82
	return registered, err
83
}
84

85
// magicMatch compares header, starting at the specified offset, masked with
86
// mask, against the magic value
87
func magicMatch(header []byte, offset int, mask, magic []byte) bool {
88
	mismatch := 0
89
	for i := offset; i < offset+len(magic); i++ {
90
		if i >= len(header) {
91
			break
92
		}
93
		m := magic[i-offset]
94
		if len(mask) > i-offset {
95
			m &= mask[i-offset]
96
		}
97
		if header[i] != m {
98
			// mismatch
99
			break
100
		}
101
		mismatch = i + 1
102
	}
103
	return mismatch >= offset+len(magic)
104
}
105

106
// parseBinfmtMisc parses a binfmt_misc registry entry.  It returns the offset,
107
// magic, and mask values, or an error if there was an error parsing the data.
108
// If the returned offset is negative, the entry was disabled or should be
109
// non-fatally ignored for some other reason.
110
func parseBinfmtMisc(path string, r io.Reader) (int, []byte, []byte, error) {
111
	offset := 0
112
	magicString, maskString := "", ""
113
	scanner := bufio.NewScanner(r)
114
	for scanner.Scan() {
115
		text := scanner.Text()
116
		if strings.TrimSpace(text) == "" {
117
			continue
118
		}
119
		fields := strings.Fields(text)
120
		switch fields[0] {
121
		case "disabled":
122
			return -1, nil, nil, nil // we should ignore this specific one
123
		case "enabled": // keep scanning this entry
124
		case "interpreter": // good, but not something we need to record
125
		case "offset":
126
			if len(fields) != 2 {
127
				return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
128
			}
129
			offset64, err := strconv.ParseInt(fields[1], 10, 8)
130
			if err != nil {
131
				return -1, nil, nil, fmt.Errorf("invalid offset %q in %q", fields[1], path)
132
			}
133
			offset = int(offset64)
134
		case "magic":
135
			if len(fields) != 2 {
136
				return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
137
			}
138
			magicString = fields[1]
139
		case "mask":
140
			if len(fields) != 2 {
141
				return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
142
			}
143
			maskString = fields[1]
144
		case "flags", "flags:":
145
			if len(fields) != 2 {
146
				return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
147
			}
148
			if !strings.Contains(fields[1], "F") { // won't work in other mount namespaces, so ignore it
149
				return -1, nil, nil, nil
150
			}
151
		default:
152
			return -1, nil, nil, fmt.Errorf("unrecognized field %q in %q", fields[0], path)
153
		}
154
		continue
155
	}
156
	if magicString == "" || maskString == "" { // entry is missing some info we need here
157
		return -1, nil, nil, nil
158
	}
159
	magic, err := hex.DecodeString(magicString)
160
	if err != nil {
161
		return -1, nil, nil, fmt.Errorf("invalid magic %q in %q", magicString, path)
162
	}
163
	mask, err := hex.DecodeString(maskString)
164
	if err != nil {
165
		return -1, nil, nil, fmt.Errorf("invalid mask %q in %q", maskString, path)
166
	}
167
	return offset, magic, mask, nil
168
}
169

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.