blob: 19898f1f3d0b0aa8fa73ba589dc3430c767dbb50 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brunc7b036b2023-06-01 12:23:57 +02004package kmod
5
6import (
7 "errors"
8 "fmt"
9 "log"
10 "sort"
11 "strings"
12
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020013 kmodpb "source.monogon.dev/osbase/kmod/spec"
Lorenz Brunc7b036b2023-06-01 12:23:57 +020014)
15
16// LookupModules looks up all matching modules for a given modalias device
17// identifier.
18func LookupModules(meta *kmodpb.Meta, modalias string) (mods []*kmodpb.Module) {
19 matches := make(map[uint32]bool)
20 lookupModulesRec(meta.ModuleDeviceMatches, modalias, matches)
21 for idx := range matches {
22 mods = append(mods, meta.Modules[idx])
23 }
24 sort.Slice(mods, func(i, j int) bool { return mods[i].Name < mods[j].Name })
25 return
26}
27
28func lookupModulesRec(n *kmodpb.RadixNode, needle string, matches map[uint32]bool) {
29 for _, c := range n.Children {
30 switch c.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010031 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020032 if len(needle) < len(c.Literal) {
33 continue
34 }
35 if c.Literal == needle[:len(c.Literal)] {
36 lookupModulesRec(c, needle[len(c.Literal):], matches)
37 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010038 case kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020039 for i := 0; i <= len(needle); i++ {
40 lookupModulesRec(c, needle[i:], matches)
41 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010042 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020043 if len(needle) < 1 {
44 continue
45 }
46 lookupModulesRec(c, needle[1:], matches)
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010047 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020048 if len(needle) < 1 {
49 continue
50 }
51 if needle[0] >= byte(c.StartByte) && needle[0] <= byte(c.EndByte) {
52 lookupModulesRec(c, needle[1:], matches)
53 }
54 }
55 }
56 if len(needle) == 0 {
57 for _, mi := range n.ModuleIndex {
58 matches[mi] = true
59 }
60 }
Lorenz Brunc7b036b2023-06-01 12:23:57 +020061}
62
63// AddPattern adds a new pattern associated with a moduleIndex to the radix tree
64// rooted at root.
65func AddPattern(root *kmodpb.RadixNode, pattern string, moduleIndex uint32) error {
66 pp, err := parsePattern(pattern)
67 if err != nil {
68 return fmt.Errorf("error parsing pattern %q: %w", pattern, err)
69 }
70 if len(pp) > 0 {
71 pp[len(pp)-1].ModuleIndex = []uint32{moduleIndex}
72 } else {
73 // This exists to handle empty patterns, which have little use in
74 // practice (but their behavior is well-defined). It exists primarily
75 // to not crash in that case as well as to appease the Fuzzer.
76 root.ModuleIndex = append(root.ModuleIndex, moduleIndex)
77 }
78 return addPatternRec(root, pp, nil)
79}
80
81// addPatternRec recursively adds a new pattern to the radix tree.
82// If currPartOverride is non-nil it is used instead of the first part in the
83// parts array.
84func addPatternRec(n *kmodpb.RadixNode, parts []*kmodpb.RadixNode, currPartOverride *kmodpb.RadixNode) error {
85 if len(parts) == 0 {
86 return nil
87 }
88 var currPart *kmodpb.RadixNode
89 if currPartOverride != nil {
90 currPart = currPartOverride
91 } else {
92 currPart = parts[0]
93 }
94 for _, c := range n.Children {
95 if c.Type != currPart.Type {
96 continue
97 }
98 switch c.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010099 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200100 if c.Literal[0] == currPart.Literal[0] {
101 var i int
102 for i < len(c.Literal) && i < len(currPart.Literal) && c.Literal[i] == currPart.Literal[i] {
103 i++
104 }
105 if i == len(c.Literal) && i == len(currPart.Literal) {
106 if len(parts) == 1 {
107 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
108 return nil
109 }
110 return addPatternRec(c, parts[1:], nil)
111 }
112 if i == len(c.Literal) {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100113 return addPatternRec(c, parts, &kmodpb.RadixNode{Type: kmodpb.RadixNode_TYPE_LITERAL, Literal: currPart.Literal[i:], ModuleIndex: currPart.ModuleIndex})
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200114 }
115 // Split current node
116 splitOldPart := &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100117 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200118 Literal: c.Literal[i:],
119 Children: c.Children,
120 ModuleIndex: c.ModuleIndex,
121 }
122 var splitNewPart *kmodpb.RadixNode
123 // Current part is a strict subset of the node being traversed
124 if i == len(currPart.Literal) {
125 if len(parts) < 2 {
126 c.Children = []*kmodpb.RadixNode{splitOldPart}
127 c.Literal = currPart.Literal
128 c.ModuleIndex = currPart.ModuleIndex
129 return nil
130 }
131 splitNewPart = parts[1]
132 parts = parts[1:]
133 } else {
134 splitNewPart = &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100135 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200136 Literal: currPart.Literal[i:],
137 ModuleIndex: currPart.ModuleIndex,
138 }
139 }
140 c.Children = []*kmodpb.RadixNode{
141 splitOldPart,
142 splitNewPart,
143 }
144 c.Literal = currPart.Literal[:i]
145 c.ModuleIndex = nil
146 return addPatternRec(splitNewPart, parts[1:], nil)
147 }
148
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100149 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200150 if c.StartByte == currPart.StartByte && c.EndByte == currPart.EndByte {
151 if len(parts) == 1 {
152 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
153 }
154 return addPatternRec(c, parts[1:], nil)
155 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100156 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD, kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200157 if len(parts) == 1 {
158 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
159 }
160 return addPatternRec(c, parts[1:], nil)
161 }
162 }
163 // No child or common prefix found, append node
164 n.Children = append(n.Children, currPart)
165 return addPatternRec(currPart, parts[1:], nil)
166}
167
168// PrintTree prints the tree from the given root node to standard out.
169// The output is not stable and should only be used for debugging/diagnostics.
170// It will log and exit the process if it encounters invalid nodes.
171func PrintTree(r *kmodpb.RadixNode) {
172 printTree(r, 0, false)
173}
174
175func printTree(r *kmodpb.RadixNode, indent int, noIndent bool) {
176 if !noIndent {
177 for i := 0; i < indent; i++ {
178 fmt.Print(" ")
179 }
180 }
181 if len(r.ModuleIndex) > 0 {
182 fmt.Printf("%v ", r.ModuleIndex)
183 }
184 switch r.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100185 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200186 fmt.Printf("%q: ", r.Literal)
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100187 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200188 fmt.Printf("?: ")
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100189 case kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200190 fmt.Printf("*: ")
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100191 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200192 fmt.Printf("[%c-%c]: ", rune(r.StartByte), rune(r.EndByte))
193 default:
194 log.Fatalf("Unknown tree type %T\n", r)
195 }
196 if len(r.Children) == 1 {
197 printTree(r.Children[0], indent, true)
198 return
199 }
200 fmt.Println("")
201 for _, c := range r.Children {
202 printTree(c, indent+1, false)
203 }
204}
205
206// parsePattern parses a string pattern into a non-hierarchical list of
207// RadixNodes. These nodes can then be futher modified and integrated into
208// a Radix tree.
209func parsePattern(pattern string) ([]*kmodpb.RadixNode, error) {
210 var out []*kmodpb.RadixNode
211 var i int
212 var currentLiteral strings.Builder
213 storeCurrentLiteral := func() {
214 if currentLiteral.Len() > 0 {
215 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100216 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200217 Literal: currentLiteral.String(),
218 })
219 currentLiteral.Reset()
220 }
221 }
222 for i < len(pattern) {
223 switch pattern[i] {
224 case '*':
225 storeCurrentLiteral()
226 i += 1
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100227 if len(out) > 0 && out[len(out)-1].Type == kmodpb.RadixNode_TYPE_WILDCARD {
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200228 continue
229 }
230 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100231 Type: kmodpb.RadixNode_TYPE_WILDCARD,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200232 })
233 case '?':
234 storeCurrentLiteral()
235 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100236 Type: kmodpb.RadixNode_TYPE_SINGLE_WILDCARD,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200237 })
238 i += 1
239 case '[':
240 storeCurrentLiteral()
241 if len(pattern) <= i+4 {
242 return nil, errors.New("illegal byte range notation, not enough characters")
243 }
244 if pattern[i+2] != '-' || pattern[i+4] != ']' {
245 return nil, errors.New("illegal byte range notation, incorrect dash or closing character")
246 }
247 nn := &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100248 Type: kmodpb.RadixNode_TYPE_BYTE_RANGE,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200249 StartByte: uint32(pattern[i+1]),
250 EndByte: uint32(pattern[i+3]),
251 }
252 if nn.StartByte > nn.EndByte {
253 return nil, errors.New("byte range start byte larger than end byte")
254 }
255 out = append(out, nn)
256 i += 5
257 case '\\':
258 if len(pattern) <= i+1 {
259 return nil, errors.New("illegal escape character at the end of the string")
260 }
261 currentLiteral.WriteByte(pattern[i+1])
262 i += 2
263 default:
264 currentLiteral.WriteByte(pattern[i])
265 i += 1
266 }
267 }
268 storeCurrentLiteral()
269 return out, nil
270}