func (k *Kmod) readModulesDep(dir string, nameToPathMapping *Bimap) (map[string][]string, error) {
	f, err := os.Open(path.Join(dir, "modules.dep"))
	if err != nil {
		return nil, err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	modules := make(map[string][]string)
	for scanner.Scan() {
		line := scanner.Text()
		idx := strings.Index(line, ":")
		if idx == -1 {
			return nil, fmt.Errorf("Cannot parse a line from modules.dep file: %v", line)
		}
		koPath := line[:idx]
		name, ok := nameToPathMapping.reverse[koPath]
		if !ok {
			return nil, fmt.Errorf("modules.dep: unable to resolve module name for %v", koPath)
		}
		if idx != len(line)-1 {
			deps := strings.Split(line[idx+2:], " ")
			for i, d := range deps {
				modName, ok := nameToPathMapping.reverse[d]
				if !ok {
					return nil, fmt.Errorf("modules.dep: unable to resolve module name for %v", d)
				}
				deps[i] = modName
			}
			modules[name] = deps
		}
	}
	return modules, scanner.Err()
}
TODO: read modules.bin file using following logic https://github.com/vadmium/module-init-tools/blob/master/index.c#L253
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()
}
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
}
func (k *Kmod) addModulesToImage(img *Image) error {
	var wg sync.WaitGroup
	modNum := len(k.requiredModules)
	errCh := make(chan error, modNum)
	unpackModule := func(modName string) {
		defer wg.Done()
		p, ok := k.nameToPathMapping.forward[modName]
		if !ok {
			errCh <- fmt.Errorf("unable to find module file for %s", modName)
		}
		modulePath := path.Join(k.hostModulesDir, p)
		f, err := os.Open(modulePath)
		if err != nil {
			errCh <- fmt.Errorf("%s: %v", modulePath, err)
			return
		}
		defer f.Close()
		var r io.Reader
		ext := path.Ext(p)
		switch ext {
		case ".ko":
			r = f
		case ".xz":
			r, err = xz.NewReader(f, 0)
		case ".zst":
			r, err = zstd.NewReader(f)
		case ".lz4":
			r = lz4.NewReader(f)
		case ".gz":
			r, err = gzip.NewReader(f)
		default:
			err = fmt.Errorf("unknown module compression format: %s", ext)
		}
		if err != nil {
			errCh <- fmt.Errorf("unpacking module %s: %v", modName, err)
			return
		}
		content, err := io.ReadAll(r)
		if err != nil {
			errCh <- fmt.Errorf("unpacking module %s: %v", modName, err)
			return
		}
		if err := img.AppendContent(imageModulesDir+modName+".ko", 0644, content); err != nil {
			errCh <- err
			return
		}
		ef, err := elf.NewFile(bytes.NewReader(content))
		if err != nil {
			errCh <- err
			return
		}
		fws, err := readModuleFirmwareRequirements(ef)
		if err != nil {
			errCh <- err
			return
		}
		if err := img.appendFirmwareFiles(modName, fws); err != nil {
			errCh <- err
			return
		}
	}
	builtinFw, err := readBuiltinModinfo(k.hostModulesDir, "firmware")
	if err != nil {
		return err
	}
	for modName := range k.requiredModules {
		if k.builtinModules[modName] {
			if err := img.appendFirmwareFiles(modName, builtinFw[modName]); err != nil {
				return err
			}
		} else {
			wg.Add(1)
			go unpackModule(modName)
		}
	}
	for m := range k.hostModules {
		if !k.requiredModules[m] {
			debug("module '%s' currently used at the host but was not added to the image", m)
		}
	}
	wg.Wait()
	select {
	case err := <-errCh:
		return err  
	default:
		return nil
	}
}
func (k *Kmod) scanModulesDir() error {
	return filepath.Walk(k.hostModulesDir, func(filename string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if info.Name() == "build" && filename == path.Join(k.hostModulesDir, "build") {
				return filepath.SkipDir
			}
			return nil
		}
		parts := strings.Split(info.Name(), ".")
		if len(parts) < 2 || len(parts) > 3 || parts[1] != "ko" {
			return nil
		}
		modName := normalizeModuleName(parts[0])
		relativePath := filename[len(k.hostModulesDir)+1:]
		var aliases []string
		if len(parts) == 3 {
			compressionSuffix := "." + parts[2]
			aliases = []string{strings.TrimSuffix(relativePath, compressionSuffix)}
		}
		return k.nameToPathMapping.Add(modName, relativePath, aliases...)
	})
}
func (k *Kmod) readModuleBuiltin() error {
	f, err := os.Open(path.Join(k.hostModulesDir, "modules.builtin"))
	if err != nil {
		return err
	}
	defer f.Close()
	for s := bufio.NewScanner(f); s.Scan(); {
		filename := s.Text()
		module := path.Base(filename)
		if !strings.HasSuffix(module, ".ko") {
			return fmt.Errorf("modules.builtin contains module filename that does not have *.ko extension: %s", filename)
		}
		modName := normalizeModuleName(module[:len(module)-3])
		k.builtinModules[modName] = true
		if err := k.nameToPathMapping.Add(modName, filename); err != nil {
			return err
		}
	}
	return nil
}