| Tim Windelschmidt | 6d33a43 | 2025-02-04 14:34:25 +0100 | [diff] [blame^] | 1 | // Copyright The Monogon Project Authors. |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 4 | package kmod |
| 5 | |
| 6 | import ( |
| 7 | "errors" |
| 8 | "fmt" |
| 9 | "log" |
| 10 | "sort" |
| 11 | "strings" |
| 12 | |
| Tim Windelschmidt | 9f21f53 | 2024-05-07 15:14:20 +0200 | [diff] [blame] | 13 | kmodpb "source.monogon.dev/osbase/kmod/spec" |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 14 | ) |
| 15 | |
| 16 | // LookupModules looks up all matching modules for a given modalias device |
| 17 | // identifier. |
| 18 | func 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 | |
| 28 | func lookupModulesRec(n *kmodpb.RadixNode, needle string, matches map[uint32]bool) { |
| 29 | for _, c := range n.Children { |
| 30 | switch c.Type { |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 31 | case kmodpb.RadixNode_TYPE_LITERAL: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 32 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 38 | case kmodpb.RadixNode_TYPE_WILDCARD: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 39 | for i := 0; i <= len(needle); i++ { |
| 40 | lookupModulesRec(c, needle[i:], matches) |
| 41 | } |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 42 | case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 43 | if len(needle) < 1 { |
| 44 | continue |
| 45 | } |
| 46 | lookupModulesRec(c, needle[1:], matches) |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 47 | case kmodpb.RadixNode_TYPE_BYTE_RANGE: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 48 | 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 Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | // AddPattern adds a new pattern associated with a moduleIndex to the radix tree |
| 64 | // rooted at root. |
| 65 | func 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. |
| 84 | func 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 99 | case kmodpb.RadixNode_TYPE_LITERAL: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 100 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 113 | return addPatternRec(c, parts, &kmodpb.RadixNode{Type: kmodpb.RadixNode_TYPE_LITERAL, Literal: currPart.Literal[i:], ModuleIndex: currPart.ModuleIndex}) |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 114 | } |
| 115 | // Split current node |
| 116 | splitOldPart := &kmodpb.RadixNode{ |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 117 | Type: kmodpb.RadixNode_TYPE_LITERAL, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 118 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 135 | Type: kmodpb.RadixNode_TYPE_LITERAL, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 136 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 149 | case kmodpb.RadixNode_TYPE_BYTE_RANGE: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 150 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 156 | case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD, kmodpb.RadixNode_TYPE_WILDCARD: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 157 | 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. |
| 171 | func PrintTree(r *kmodpb.RadixNode) { |
| 172 | printTree(r, 0, false) |
| 173 | } |
| 174 | |
| 175 | func 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 185 | case kmodpb.RadixNode_TYPE_LITERAL: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 186 | fmt.Printf("%q: ", r.Literal) |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 187 | case kmodpb.RadixNode_TYPE_SINGLE_WILDCARD: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 188 | fmt.Printf("?: ") |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 189 | case kmodpb.RadixNode_TYPE_WILDCARD: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 190 | fmt.Printf("*: ") |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 191 | case kmodpb.RadixNode_TYPE_BYTE_RANGE: |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 192 | 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. |
| 209 | func 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 216 | Type: kmodpb.RadixNode_TYPE_LITERAL, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 217 | 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 227 | if len(out) > 0 && out[len(out)-1].Type == kmodpb.RadixNode_TYPE_WILDCARD { |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 228 | continue |
| 229 | } |
| 230 | out = append(out, &kmodpb.RadixNode{ |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 231 | Type: kmodpb.RadixNode_TYPE_WILDCARD, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 232 | }) |
| 233 | case '?': |
| 234 | storeCurrentLiteral() |
| 235 | out = append(out, &kmodpb.RadixNode{ |
| Tim Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 236 | Type: kmodpb.RadixNode_TYPE_SINGLE_WILDCARD, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 237 | }) |
| 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 Windelschmidt | a10d0cb | 2025-01-13 14:44:15 +0100 | [diff] [blame] | 248 | Type: kmodpb.RadixNode_TYPE_BYTE_RANGE, |
| Lorenz Brun | c7b036b | 2023-06-01 12:23:57 +0200 | [diff] [blame] | 249 | 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 | } |