blob: 1631d8766a134e0f788a0ac973ae76ab5a4458a7 [file] [log] [blame]
Lorenz Brunc7b036b2023-06-01 12:23:57 +02001package kmod
2
3import (
4 "errors"
5 "fmt"
6 "log"
7 "sort"
8 "strings"
9
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020010 kmodpb "source.monogon.dev/osbase/kmod/spec"
Lorenz Brunc7b036b2023-06-01 12:23:57 +020011)
12
13// LookupModules looks up all matching modules for a given modalias device
14// identifier.
15func LookupModules(meta *kmodpb.Meta, modalias string) (mods []*kmodpb.Module) {
16 matches := make(map[uint32]bool)
17 lookupModulesRec(meta.ModuleDeviceMatches, modalias, matches)
18 for idx := range matches {
19 mods = append(mods, meta.Modules[idx])
20 }
21 sort.Slice(mods, func(i, j int) bool { return mods[i].Name < mods[j].Name })
22 return
23}
24
25func lookupModulesRec(n *kmodpb.RadixNode, needle string, matches map[uint32]bool) {
26 for _, c := range n.Children {
27 switch c.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010028 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020029 if len(needle) < len(c.Literal) {
30 continue
31 }
32 if c.Literal == needle[:len(c.Literal)] {
33 lookupModulesRec(c, needle[len(c.Literal):], matches)
34 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010035 case kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020036 for i := 0; i <= len(needle); i++ {
37 lookupModulesRec(c, needle[i:], matches)
38 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010039 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020040 if len(needle) < 1 {
41 continue
42 }
43 lookupModulesRec(c, needle[1:], matches)
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010044 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020045 if len(needle) < 1 {
46 continue
47 }
48 if needle[0] >= byte(c.StartByte) && needle[0] <= byte(c.EndByte) {
49 lookupModulesRec(c, needle[1:], matches)
50 }
51 }
52 }
53 if len(needle) == 0 {
54 for _, mi := range n.ModuleIndex {
55 matches[mi] = true
56 }
57 }
Lorenz Brunc7b036b2023-06-01 12:23:57 +020058}
59
60// AddPattern adds a new pattern associated with a moduleIndex to the radix tree
61// rooted at root.
62func AddPattern(root *kmodpb.RadixNode, pattern string, moduleIndex uint32) error {
63 pp, err := parsePattern(pattern)
64 if err != nil {
65 return fmt.Errorf("error parsing pattern %q: %w", pattern, err)
66 }
67 if len(pp) > 0 {
68 pp[len(pp)-1].ModuleIndex = []uint32{moduleIndex}
69 } else {
70 // This exists to handle empty patterns, which have little use in
71 // practice (but their behavior is well-defined). It exists primarily
72 // to not crash in that case as well as to appease the Fuzzer.
73 root.ModuleIndex = append(root.ModuleIndex, moduleIndex)
74 }
75 return addPatternRec(root, pp, nil)
76}
77
78// addPatternRec recursively adds a new pattern to the radix tree.
79// If currPartOverride is non-nil it is used instead of the first part in the
80// parts array.
81func addPatternRec(n *kmodpb.RadixNode, parts []*kmodpb.RadixNode, currPartOverride *kmodpb.RadixNode) error {
82 if len(parts) == 0 {
83 return nil
84 }
85 var currPart *kmodpb.RadixNode
86 if currPartOverride != nil {
87 currPart = currPartOverride
88 } else {
89 currPart = parts[0]
90 }
91 for _, c := range n.Children {
92 if c.Type != currPart.Type {
93 continue
94 }
95 switch c.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +010096 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +020097 if c.Literal[0] == currPart.Literal[0] {
98 var i int
99 for i < len(c.Literal) && i < len(currPart.Literal) && c.Literal[i] == currPart.Literal[i] {
100 i++
101 }
102 if i == len(c.Literal) && i == len(currPart.Literal) {
103 if len(parts) == 1 {
104 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
105 return nil
106 }
107 return addPatternRec(c, parts[1:], nil)
108 }
109 if i == len(c.Literal) {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100110 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 +0200111 }
112 // Split current node
113 splitOldPart := &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100114 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200115 Literal: c.Literal[i:],
116 Children: c.Children,
117 ModuleIndex: c.ModuleIndex,
118 }
119 var splitNewPart *kmodpb.RadixNode
120 // Current part is a strict subset of the node being traversed
121 if i == len(currPart.Literal) {
122 if len(parts) < 2 {
123 c.Children = []*kmodpb.RadixNode{splitOldPart}
124 c.Literal = currPart.Literal
125 c.ModuleIndex = currPart.ModuleIndex
126 return nil
127 }
128 splitNewPart = parts[1]
129 parts = parts[1:]
130 } else {
131 splitNewPart = &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100132 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200133 Literal: currPart.Literal[i:],
134 ModuleIndex: currPart.ModuleIndex,
135 }
136 }
137 c.Children = []*kmodpb.RadixNode{
138 splitOldPart,
139 splitNewPart,
140 }
141 c.Literal = currPart.Literal[:i]
142 c.ModuleIndex = nil
143 return addPatternRec(splitNewPart, parts[1:], nil)
144 }
145
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100146 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200147 if c.StartByte == currPart.StartByte && c.EndByte == currPart.EndByte {
148 if len(parts) == 1 {
149 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
150 }
151 return addPatternRec(c, parts[1:], nil)
152 }
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100153 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD, kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200154 if len(parts) == 1 {
155 c.ModuleIndex = append(c.ModuleIndex, parts[0].ModuleIndex...)
156 }
157 return addPatternRec(c, parts[1:], nil)
158 }
159 }
160 // No child or common prefix found, append node
161 n.Children = append(n.Children, currPart)
162 return addPatternRec(currPart, parts[1:], nil)
163}
164
165// PrintTree prints the tree from the given root node to standard out.
166// The output is not stable and should only be used for debugging/diagnostics.
167// It will log and exit the process if it encounters invalid nodes.
168func PrintTree(r *kmodpb.RadixNode) {
169 printTree(r, 0, false)
170}
171
172func printTree(r *kmodpb.RadixNode, indent int, noIndent bool) {
173 if !noIndent {
174 for i := 0; i < indent; i++ {
175 fmt.Print(" ")
176 }
177 }
178 if len(r.ModuleIndex) > 0 {
179 fmt.Printf("%v ", r.ModuleIndex)
180 }
181 switch r.Type {
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100182 case kmodpb.RadixNode_TYPE_LITERAL:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200183 fmt.Printf("%q: ", r.Literal)
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100184 case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200185 fmt.Printf("?: ")
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100186 case kmodpb.RadixNode_TYPE_WILDCARD:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200187 fmt.Printf("*: ")
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100188 case kmodpb.RadixNode_TYPE_BYTE_RANGE:
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200189 fmt.Printf("[%c-%c]: ", rune(r.StartByte), rune(r.EndByte))
190 default:
191 log.Fatalf("Unknown tree type %T\n", r)
192 }
193 if len(r.Children) == 1 {
194 printTree(r.Children[0], indent, true)
195 return
196 }
197 fmt.Println("")
198 for _, c := range r.Children {
199 printTree(c, indent+1, false)
200 }
201}
202
203// parsePattern parses a string pattern into a non-hierarchical list of
204// RadixNodes. These nodes can then be futher modified and integrated into
205// a Radix tree.
206func parsePattern(pattern string) ([]*kmodpb.RadixNode, error) {
207 var out []*kmodpb.RadixNode
208 var i int
209 var currentLiteral strings.Builder
210 storeCurrentLiteral := func() {
211 if currentLiteral.Len() > 0 {
212 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100213 Type: kmodpb.RadixNode_TYPE_LITERAL,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200214 Literal: currentLiteral.String(),
215 })
216 currentLiteral.Reset()
217 }
218 }
219 for i < len(pattern) {
220 switch pattern[i] {
221 case '*':
222 storeCurrentLiteral()
223 i += 1
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100224 if len(out) > 0 && out[len(out)-1].Type == kmodpb.RadixNode_TYPE_WILDCARD {
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200225 continue
226 }
227 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100228 Type: kmodpb.RadixNode_TYPE_WILDCARD,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200229 })
230 case '?':
231 storeCurrentLiteral()
232 out = append(out, &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100233 Type: kmodpb.RadixNode_TYPE_SINGLE_WILDCARD,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200234 })
235 i += 1
236 case '[':
237 storeCurrentLiteral()
238 if len(pattern) <= i+4 {
239 return nil, errors.New("illegal byte range notation, not enough characters")
240 }
241 if pattern[i+2] != '-' || pattern[i+4] != ']' {
242 return nil, errors.New("illegal byte range notation, incorrect dash or closing character")
243 }
244 nn := &kmodpb.RadixNode{
Tim Windelschmidta10d0cb2025-01-13 14:44:15 +0100245 Type: kmodpb.RadixNode_TYPE_BYTE_RANGE,
Lorenz Brunc7b036b2023-06-01 12:23:57 +0200246 StartByte: uint32(pattern[i+1]),
247 EndByte: uint32(pattern[i+3]),
248 }
249 if nn.StartByte > nn.EndByte {
250 return nil, errors.New("byte range start byte larger than end byte")
251 }
252 out = append(out, nn)
253 i += 5
254 case '\\':
255 if len(pattern) <= i+1 {
256 return nil, errors.New("illegal escape character at the end of the string")
257 }
258 currentLiteral.WriteByte(pattern[i+1])
259 i += 2
260 default:
261 currentLiteral.WriteByte(pattern[i])
262 i += 1
263 }
264 }
265 storeCurrentLiteral()
266 return out, nil
267}