From 4c1f201b6fc2cc30625d3c706b1f45cc68ef0223 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 11 Jan 2023 19:37:37 +0100 Subject: pkg/subsystem/linux: add the basic subsystem extraction code --- pkg/subsystem/linux/subsystems.go | 124 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 pkg/subsystem/linux/subsystems.go (limited to 'pkg/subsystem/linux/subsystems.go') diff --git a/pkg/subsystem/linux/subsystems.go b/pkg/subsystem/linux/subsystems.go new file mode 100644 index 000000000..b5ffa924c --- /dev/null +++ b/pkg/subsystem/linux/subsystems.go @@ -0,0 +1,124 @@ +// Copyright 2023 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package linux + +import ( + "fmt" + "io/fs" + "os" + "sort" + + "github.com/google/syzkaller/pkg/subsystem/entity" +) + +func ListFromRepo(repo string) ([]*entity.Subsystem, error) { + return listFromRepoInner(os.DirFS(repo), linuxSubsystemRules) +} + +// listFromRepoInner allows for better testing. +func listFromRepoInner(root fs.FS, rules *customRules) ([]*entity.Subsystem, error) { + records, err := getMaintainers(root) + if err != nil { + return nil, err + } + ctx := &linuxCtx{ + root: root, + rawRecords: records, + extraRules: rules, + } + ctx.groupByList() + list, err := ctx.getSubsystems() + if err != nil { + return nil, err + } + // Sort subsystems by name to keep output consistent. + sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name }) + // Sort path rules to keep output consistent. + for _, entity := range list { + sort.Slice(entity.PathRules, func(i, j int) bool { + a, b := entity.PathRules[i], entity.PathRules[j] + if a.IncludeRegexp != b.IncludeRegexp { + return a.IncludeRegexp < b.IncludeRegexp + } + return a.ExcludeRegexp < b.ExcludeRegexp + }) + } + return list, nil +} + +type linuxCtx struct { + root fs.FS + rawRecords []*maintainersRecord + extraRules *customRules +} + +type subsystemCandidate struct { + records []*maintainersRecord + commonEmail string +} + +func (ctx *linuxCtx) groupByList() []*subsystemCandidate { + perList := make(map[string][]*maintainersRecord) + for _, record := range ctx.rawRecords { + for _, list := range record.lists { + perList[list] = append(perList[list], record) + } + } + ret := []*subsystemCandidate{} + for email, list := range perList { + ret = append(ret, &subsystemCandidate{ + commonEmail: email, + records: list, + }) + } + return ret +} + +func (ctx *linuxCtx) getSubsystems() ([]*entity.Subsystem, error) { + ret := []*entity.Subsystem{} + for _, raw := range ctx.groupByList() { + s := &entity.Subsystem{} + mergeRawRecords(s, raw.records) + ret = append(ret, s) + } + return ret, nil +} + +func mergeRawRecords(subsystem *entity.Subsystem, records []*maintainersRecord) { + unique := func(list []string) []string { + m := make(map[string]struct{}) + for _, s := range list { + m[s] = struct{}{} + } + ret := []string{} + for s := range m { + ret = append(ret, s) + } + return ret + } + var lists, maintainers []string + for _, record := range records { + rule := record.ToPathRule() + if !rule.IsEmpty() { + subsystem.PathRules = append(subsystem.PathRules, rule) + } + lists = append(lists, record.lists...) + maintainers = append(maintainers, record.maintainers...) + } + subsystem.Lists = unique(lists) + // But there's a risk that we collect too many unrelated maintainers, so + // let's only merge them if there are no lists. + if len(records) <= 1 { + subsystem.Maintainers = unique(maintainers) + } +} + +func getMaintainers(root fs.FS) ([]*maintainersRecord, error) { + f, err := root.Open("MAINTAINERS") + if err != nil { + return nil, fmt.Errorf("failed to open the MAINTAINERS file: %w", err) + } + defer f.Close() + return parseLinuxMaintainers(f) +} -- cgit mrf-deployment