diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-01-17 19:20:37 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2023-02-10 14:34:44 +0100 |
| commit | ba1c7407eaa0c09e93d8f319c9e7e65bdf0187d3 (patch) | |
| tree | f7348e12a2c86a7e1858412c4b206882d15b2003 /pkg/subsystem/linux | |
| parent | 4c1f201b6fc2cc30625d3c706b1f45cc68ef0223 (diff) | |
pkg/subsystem/linux: extract names for subsystems
Extract the short subsystem name from the mailing list email.
Stip the common prefixes and suffixes and make sure there are no
duplicates.
As a fallback, assign the whole list email address as a subsystem name.
Diffstat (limited to 'pkg/subsystem/linux')
| -rw-r--r-- | pkg/subsystem/linux/names.go | 103 | ||||
| -rw-r--r-- | pkg/subsystem/linux/names_test.go | 115 | ||||
| -rw-r--r-- | pkg/subsystem/linux/subsystems.go | 4 | ||||
| -rw-r--r-- | pkg/subsystem/linux/subsystems_test.go | 4 |
4 files changed, 226 insertions, 0 deletions
diff --git a/pkg/subsystem/linux/names.go b/pkg/subsystem/linux/names.go new file mode 100644 index 000000000..0f422ea05 --- /dev/null +++ b/pkg/subsystem/linux/names.go @@ -0,0 +1,103 @@ +// 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" + "regexp" + + "github.com/google/syzkaller/pkg/subsystem/entity" +) + +// setSubsystemNames assigns unique names to the presented subsystems. +// If it failed to assign a name to a subsystem, the Name field remains empty. +func setSubsystemNames(list []*entity.Subsystem) error { + dups := map[string]bool{} + for _, item := range list { + // For now, we can only infer name from the list email. + if len(item.Lists) == 0 { + return fmt.Errorf("no lists for %#v", item) + } + email := item.Lists[0] + name := emailToName(email) + if !validateName(name) { + return fmt.Errorf("failed to extract a name from %s", email) + } + if dups[name] { + return fmt.Errorf("duplicate subsystem name: %s", item.Name) + } + item.Name = name + dups[name] = true + } + return nil +} + +func validateName(name string) bool { + const ( + minLen = 2 + maxLen = 16 // otherwise the email subject can get too big + ) + return len(name) >= minLen && len(name) <= maxLen +} + +func emailToName(email string) string { + if name := emailExceptions[email]; name != "" { + return name + } + ret := emailStripRe.FindStringSubmatch(email) + if ret == nil { + return "" + } + return ret[1] +} + +func buildEmailStripRe() *regexp.Regexp { + raw := `^(?:` + for i := 0; i < len(stripPrefixes); i++ { + if i > 0 { + raw += "|" + } + raw += regexp.QuoteMeta(stripPrefixes[i]) + } + raw += ")*(.*?)(?:" + for i := 0; i < len(stripSuffixes); i++ { + if i > 0 { + raw += "|" + } + raw += regexp.QuoteMeta(stripSuffixes[i]) + } + raw += ")*@.*$" + return regexp.MustCompile(raw) +} + +var ( + emailExceptions = map[string]string{ + "patches@opensource.cirrus.com": "cirrus", + "virtualization@lists.linux-foundation.org": "virt", // the name is too long + "dev@openvswitch.org": "openvswitch", + "devel@acpica.org": "acpica", + "kernel@dh-electronics.com": "dh-electr", + "devel@lists.orangefs.org": "orangefs", + "linux-arm-kernel@axis.com": "axis", + "Dell.Client.Kernel@dell.com": "dell", + "sound-open-firmware@alsa-project.org": "sof", + "platform-driver-x86@vger.kernel.org": "x86-drivers", + "linux-trace-devel@vger.kernel.org": "rt-tools", + "aws-nitro-enclaves-devel@amazon.com": "nitro", + "brcm80211-dev-list.pdl@broadcom.com": "brcm80211", + "osmocom-net-gprs@lists.osmocom.org": "osmocom", + "netdev@vger.kernel.org": "net", + "megaraidlinux.pdl@broadcom.com": "megaraid", + "mpi3mr-linuxdrv.pdl@broadcom.com": "mpi3", + "MPT-FusionLinux.pdl@broadcom.com": "mpt-fusion", + } + stripPrefixes = []string{"linux-"} + stripSuffixes = []string{ + "-devel", "-dev", "-devs", "-developer", "devel", + "-user", "-users", + "-discussion", "-discuss", "-list", "-en", + "-kernel", "-linux", "-general", + } + emailStripRe = buildEmailStripRe() +) diff --git a/pkg/subsystem/linux/names_test.go b/pkg/subsystem/linux/names_test.go new file mode 100644 index 000000000..0b2352411 --- /dev/null +++ b/pkg/subsystem/linux/names_test.go @@ -0,0 +1,115 @@ +// 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 ( + "testing" + + "github.com/google/syzkaller/pkg/subsystem/entity" +) + +func TestEmailToName(t *testing.T) { + tests := map[string]string{ + // These are following the general rules. + "linux-nilfs@vger.kernel.org": "nilfs", + "tomoyo-dev-en@lists.osdn.me": "tomoyo", + "tipc-discussion@lists.sourceforge.net": "tipc", + "v9fs-developer@lists.sourceforge.net": "v9fs", + "zd1211-devs@lists.sourceforge.net": "zd1211", + // Test that we can handle exceptions. + "virtualization@lists.linux-foundation.org": "virt", + } + for email, name := range tests { + result := emailToName(email) + if result != name { + t.Fatalf("%#v: expected %#v, got %#v", email, name, result) + } + } +} + +type subsystemTestInput struct { + email string + outName string +} + +func (sti subsystemTestInput) ToSubsystem() *entity.Subsystem { + s := &entity.Subsystem{} + if sti.email != "" { + s.Lists = append(s.Lists, sti.email) + } + return s +} + +func TestSetSubsystemNames(t *testing.T) { + tests := []struct { + name string + inputs []subsystemTestInput + mustFail bool + }{ + { + name: "plan test", + inputs: []subsystemTestInput{ + { + email: "linux-ntfs-dev@lists.sourceforge.net", + outName: "ntfs", + }, + { + email: "llvm@lists.linux.dev", + outName: "llvm", + }, + }, + }, + { + name: "has dup name", + inputs: []subsystemTestInput{ + { + email: "linux-ntfs-dev@lists.sourceforge.net", + outName: "ntfs", + }, + { + email: "ntfs@lists.sourceforge.net", + outName: "ntfs", + }, + }, + mustFail: true, + }, + { + name: "has empty list", + inputs: []subsystemTestInput{ + { + email: "linux-ntfs-dev@lists.sourceforge.net", + outName: "ntfs", + }, + { + email: "", + outName: "", + }, + }, + mustFail: true, + }, + } + for _, test := range tests { + curr := test + t.Run(curr.name, func(t *testing.T) { + list := []*entity.Subsystem{} + for _, i := range curr.inputs { + list = append(list, i.ToSubsystem()) + } + err := setSubsystemNames(list) + if curr.mustFail != (err != nil) { + t.Fatalf("expected failure: %v, got: %v", curr.mustFail, err) + } + if curr.mustFail { + return + } + for i, item := range list { + if item.Name != curr.inputs[i].outName { + t.Fatalf("invalid name for #%d: expected %#v, got %#v", + i+1, curr.inputs[i].outName, item.Name, + ) + } + } + }) + } +} diff --git a/pkg/subsystem/linux/subsystems.go b/pkg/subsystem/linux/subsystems.go index b5ffa924c..468ba5fbc 100644 --- a/pkg/subsystem/linux/subsystems.go +++ b/pkg/subsystem/linux/subsystems.go @@ -82,6 +82,9 @@ func (ctx *linuxCtx) getSubsystems() ([]*entity.Subsystem, error) { mergeRawRecords(s, raw.records) ret = append(ret, s) } + if err := setSubsystemNames(ret); err != nil { + return nil, fmt.Errorf("failed to set names: %w", err) + } return ret, nil } @@ -95,6 +98,7 @@ func mergeRawRecords(subsystem *entity.Subsystem, records []*maintainersRecord) for s := range m { ret = append(ret, s) } + sort.Strings(ret) return ret } var lists, maintainers []string diff --git a/pkg/subsystem/linux/subsystems_test.go b/pkg/subsystem/linux/subsystems_test.go index f5699f26e..caf675244 100644 --- a/pkg/subsystem/linux/subsystems_test.go +++ b/pkg/subsystem/linux/subsystems_test.go @@ -25,17 +25,21 @@ func TestGroupLinuxSubsystems(t *testing.T) { } expected := []*entity.Subsystem{ { + Name: "fs", Lists: []string{"linux-fsdevel@vger.kernel.org"}, Maintainers: []string{"email_vfs@email.com"}, }, { + Name: "ext4", Lists: []string{"linux-ext4@vger.kernel.org"}, Maintainers: []string{"email_ext4@email.com", "email_ext4_2@email.com"}, }, { + Name: "mm", Lists: []string{"linux-mm@kvack.org"}, }, { + Name: "kernel", Lists: []string{"linux-kernel@vger.kernel.org"}, Maintainers: []string{"email_rest@email.com"}, }, |
