func readBuiltinModinfo(dir string, propName string) (map[string][]string, error) {
	data, err := os.ReadFile(path.Join(dir, "modules.builtin.modinfo"))
	if err != nil {
		return nil, err
	}
	result := make(map[string][]string)
	re := regexp.MustCompile(`(\w*?).` + propName + `=([^\0]*)`)
	matches := re.FindAllSubmatch(data, -1)
	for _, m := range matches {
		name := string(m[1])
		fw := string(m[2])
		result[name] = append(result[name], fw)
	}
	return result, nil
}
readBuiltinModinfo reads builtin modules properties and returns a map of
module -> [values]
Note that values is an array as a module can contain multiple properties with the same name.
package main
import (
	"bufio"
	"bytes"
	"compress/gzip"
	"container/list"
	"debug/elf"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"github.com/klauspost/compress/zstd"
	"github.com/pierrec/lz4"
	"github.com/xi2/xz"
)
type alias struct {
	pattern, module string
}
type Kmod struct {
	universal         bool  
	kernelVersion     string
	hostModulesDir    string  
	nameToPathMapping *Bimap  
	builtinModules    set
	requiredModules   set                  
	dependencies      map[string][]string  
	postDependencies  map[string][]string  
	modprobeOptions   map[string]string    
	aliases           []alias
	extraDep          map[string][]string  
	hostModules       set
}
func NewKmod(conf *generatorConfig) (*Kmod, error) {
	kmod := &Kmod{
		universal:         conf.universal,
		kernelVersion:     conf.kernelVersion,
		hostModulesDir:    conf.modulesDir,
		nameToPathMapping: NewBimap(),
		builtinModules:    make(set),
		requiredModules:   make(set),
		aliases:           nil,
		extraDep:          make(map[string][]string),
		hostModules:       make(set),
	}
	if err := kmod.scanModulesDir(); err != nil {
		return nil, err
	}
	if err := kmod.readModuleBuiltin(); err != nil {
		return nil, err
	}
	if err := kmod.readKernelAliases(); err != nil {
		return nil, err
	}
	var err error
	kmod.hostModules, err = conf.readHostModules()
	if err != nil {
		return nil, err
	}
	kmod.modprobeOptions, err = conf.readModprobeOptions()
	if err != nil {
		return nil, err
	}
	return kmod, nil
}
func (k *Kmod) activateModules(filter, failIfMissing bool, mods ...string) error {
	filter = filter && !k.universal  
	for _, m := range mods {
		activate := true
		if m[0] == '-' {
			m = m[1:]
			activate = false  
		}
		if m == "" {
			return fmt.Errorf("invalid modules pattern %s", m)
		}
		if pattern := m; pattern == "*" || strings.HasSuffix(pattern, "/") {
			for mod, modPath := range k.nameToPathMapping.forward {
				if filter && !k.hostModules[mod] {
					continue
				}
				if pattern == "*" || strings.HasPrefix(modPath, pattern) {
					if activate {
						if !k.requiredModules[mod] {
							debug("activate module %s", mod)
							k.requiredModules[mod] = true
						}
					} else {
						if k.requiredModules[mod] {
							debug("deactivate module %s", mod)
							delete(k.requiredModules, mod)
						}
					}
				}
			}
		} else {
			if filter && !k.hostModules[m] {
				continue
			}
			var mod string
			if _, ok := k.nameToPathMapping.forward[m]; ok {
				mod = m
			} else if name, ok := k.nameToPathMapping.reverse[m]; ok {
				mod = name
			} else {
				debug("requested module %s is missing", m)
				if failIfMissing {
					return fmt.Errorf("module %s does not exist", m)
				}
				continue
			}
			if activate {
				if !k.requiredModules[mod] {
					debug("activate module %s", mod)
					k.requiredModules[mod] = true
				}
			} else {
				if k.requiredModules[mod] {
					debug("deactivate module %s", mod)
					delete(k.requiredModules, mod)
				}
			}
		}
	}
	return nil
}
func (k *Kmod) resolveDependencies() error {
	modulesDep, err := k.readModulesDep(k.hostModulesDir, k.nameToPathMapping)
	if err != nil {
		return err
	}
	softPreDeps, softPostDeps, err := k.readModulesSoftDep(k.hostModulesDir)
	if err != nil {
		return err
	}
	depsToVisit := list.New()
	for mod := range k.requiredModules {
		depsToVisit.PushBack(mod)
	}
	k.dependencies = make(map[string][]string)
	k.postDependencies = make(map[string][]string)
	depsVisited := make(set)
	for e := depsToVisit.Front(); e != nil; e = e.Next() {
		name := e.Value.(string)
		if depsVisited[name] {
			continue
		}
		depsVisited[name] = true
		k.requiredModules[name] = true
		deps := make([]string, 0)
		if d, exist := modulesDep[name]; exist {
			deps = append(deps, d...)
		}
		if d, exist := softPreDeps[name]; exist {
			deps = append(deps, d...)
		}
		if d, exist := k.extraDep[name]; exist {
			deps = append(deps, d...)
		}
		if len(deps) > 0 {
			k.dependencies[name] = deps
			for _, d := range deps {
				depsToVisit.PushBack(d)
			}
		}
		if deps, exist := softPostDeps[name]; exist {
			k.postDependencies[name] = deps
			for _, d := range deps {
				depsToVisit.PushBack(d)
			}
		}
	}
	return nil
}
func (k *Kmod) readKernelAliases() error {
	f, err := os.Open(path.Join(k.hostModulesDir, "modules.alias"))
	if err != nil {
		return err
	}
	defer f.Close()
	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		if !strings.HasPrefix(line, "alias ") {
			continue  
		}
		line = strings.TrimPrefix(line, "alias ")
		idx := strings.LastIndexByte(line, ' ')
		if idx == -1 {
			return fmt.Errorf("modules.alias line has no space: %q", line)
		}
		pattern := line[:idx]
		module := line[idx+1:]
		k.aliases = append(k.aliases, alias{pattern, module})
	}
	return s.Err()
}