aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/subsystem
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/subsystem')
-rw-r--r--pkg/subsystem/linux/maintainers.go144
-rw-r--r--pkg/subsystem/linux/maintainers_fuzz.go11
-rw-r--r--pkg/subsystem/linux/maintainers_test.go174
3 files changed, 329 insertions, 0 deletions
diff --git a/pkg/subsystem/linux/maintainers.go b/pkg/subsystem/linux/maintainers.go
new file mode 100644
index 000000000..ce692bfd4
--- /dev/null
+++ b/pkg/subsystem/linux/maintainers.go
@@ -0,0 +1,144 @@
+// 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 (
+ "bufio"
+ "fmt"
+ "io"
+ "net/mail"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+// maintainersRecord represents a single raw record in the MAINTAINERS file.
+type maintainersRecord struct {
+ name string
+ includePatterns []string
+ excludePatterns []string
+ regexps []string
+ lists []string
+ maintainers []string
+}
+
+func parseLinuxMaintainers(content io.Reader) ([]*maintainersRecord, error) {
+ scanner := bufio.NewScanner(content)
+ // First skip the headers.
+ for scanner.Scan() {
+ line := scanner.Text()
+ if line == "Maintainers List" {
+ // Also skip ------.
+ scanner.Scan()
+ break
+ }
+ }
+ ml := &maintainersLexer{scanner: scanner}
+ ret := []*maintainersRecord{}
+loop:
+ for {
+ item := ml.next()
+ switch v := item.(type) {
+ case recordTitle:
+ // The new subsystem begins.
+ ret = append(ret, &maintainersRecord{name: string(v)})
+ case recordProperty:
+ if len(ret) == 0 {
+ return nil, fmt.Errorf("line %d: property without subsystem", ml.currentLine)
+ }
+ err := applyProperty(ret[len(ret)-1], &v)
+ if err != nil {
+ return nil, fmt.Errorf("line %d: failed to apply the property %#v: %w",
+ ml.currentLine, v, err)
+ }
+ case endOfFile:
+ break loop
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ return ret, nil
+}
+
+type maintainersLexer struct {
+ scanner *bufio.Scanner
+ currentLine int
+ inComment bool
+}
+
+type recordTitle string
+type recordProperty struct {
+ key string
+ value string
+}
+type endOfFile struct{}
+
+var propertyRe = regexp.MustCompile(`^([[:alpha:]]):\s+(.*).*$`)
+
+func (ml *maintainersLexer) next() interface{} {
+ for ml.scanner.Scan() {
+ ml.currentLine++
+ rawLine := ml.scanner.Text()
+ line := strings.TrimSpace(rawLine)
+ if strings.HasPrefix(line, ".") {
+ ml.inComment = true
+ continue
+ }
+ // A comment continues to the next line(s) if they begin with a space character.
+ if ml.inComment && (line == "" || !unicode.IsSpace(rune(rawLine[0]))) {
+ ml.inComment = false
+ }
+ if ml.inComment || line == "" {
+ continue
+ }
+ // Now let's consider the possible line types.
+ if matches := propertyRe.FindStringSubmatch(line); matches != nil {
+ return recordProperty{key: matches[1], value: matches[2]}
+ }
+ return recordTitle(line)
+ }
+ return endOfFile{}
+}
+
+func applyProperty(record *maintainersRecord, property *recordProperty) error {
+ switch property.key {
+ case "F":
+ record.includePatterns = append(record.includePatterns, property.value)
+ case "X":
+ record.excludePatterns = append(record.excludePatterns, property.value)
+ case "N":
+ if _, err := regexp.Compile(property.value); err != nil {
+ return fmt.Errorf("invalid regexp: %s", property.value)
+ }
+ record.regexps = append(record.regexps, property.value)
+ case "M":
+ value, err := parseEmail(property.value)
+ if err != nil {
+ return err
+ }
+ record.maintainers = append(record.maintainers, value)
+ case "L":
+ value, err := parseEmail(property.value)
+ if err != nil {
+ return err
+ }
+ record.lists = append(record.lists, value)
+ }
+ return nil
+}
+
+func parseEmail(value string) (string, error) {
+ // Sometimes there happen extra symbols at the end of the line,
+ // let's make this parser more error tolerant.
+ pos := strings.LastIndexAny(value, ">)")
+ if pos >= 0 {
+ value = value[:pos+1]
+ }
+ addr, err := mail.ParseAddress(value)
+ if err != nil {
+ return "", err
+ }
+ return addr.Address, nil
+}
diff --git a/pkg/subsystem/linux/maintainers_fuzz.go b/pkg/subsystem/linux/maintainers_fuzz.go
new file mode 100644
index 000000000..ebf4be524
--- /dev/null
+++ b/pkg/subsystem/linux/maintainers_fuzz.go
@@ -0,0 +1,11 @@
+// Copyright 2022 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 "bytes"
+
+func Fuzz(data []byte) int {
+ parseLinuxMaintainers(bytes.NewReader(data))
+ return 0
+}
diff --git a/pkg/subsystem/linux/maintainers_test.go b/pkg/subsystem/linux/maintainers_test.go
new file mode 100644
index 000000000..874bdcaa7
--- /dev/null
+++ b/pkg/subsystem/linux/maintainers_test.go
@@ -0,0 +1,174 @@
+// 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 (
+ "strings"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func TestLinuxMaintainers(t *testing.T) {
+ result, err := parseLinuxMaintainers(
+ strings.NewReader(maintainersSample),
+ )
+ if err != nil {
+ t.Fatal(err)
+ }
+ targetResult := []*maintainersRecord{
+ {
+ name: "3C59X NETWORK DRIVER",
+ includePatterns: []string{
+ "Documentation/networking/device_drivers/ethernet/3com/vortex.rst",
+ "drivers/net/ethernet/3com/3c59x.c",
+ },
+ lists: []string{"netdev@vger.kernel.org"},
+ maintainers: []string{"email1@kernel.org"},
+ },
+ {
+ name: "ABI/API",
+ includePatterns: []string{
+ "include/linux/syscalls.h",
+ "kernel/sys_ni.c",
+ },
+ excludePatterns: []string{
+ "include/uapi/",
+ "arch/*/include/uapi/",
+ },
+ lists: []string{"linux-api@vger.kernel.org"},
+ },
+ {
+ name: "AD1889 ALSA SOUND DRIVER",
+ includePatterns: []string{"sound/pci/ad1889.*"},
+ lists: []string{"linux-parisc@vger.kernel.org"},
+ },
+ {
+ name: "PVRUSB2 VIDEO4LINUX DRIVER",
+ includePatterns: []string{
+ "Documentation/driver-api/media/drivers/pvrusb2*",
+ "drivers/media/usb/pvrusb2/",
+ },
+ lists: []string{
+ "pvrusb2@isely.net",
+ "linux-media@vger.kernel.org",
+ },
+ maintainers: []string{"email2@kernel.org"},
+ },
+ {
+ name: "RISC-V ARCHITECTURE",
+ includePatterns: []string{"arch/riscv/"},
+ regexps: []string{"riscv"},
+ lists: []string{"linux-riscv@lists.infradead.org"},
+ maintainers: []string{
+ "email3@kernel.org",
+ "email4@kernel.org",
+ "email5@kernel.org",
+ },
+ },
+ {
+ name: "THE REST",
+ includePatterns: []string{"*", "*/"},
+ lists: []string{"linux-kernel@vger.kernel.org"},
+ maintainers: []string{"email6@kernel.org"},
+ },
+ }
+ if diff := cmp.Diff(targetResult, result,
+ cmp.AllowUnexported(maintainersRecord{})); diff != "" {
+ t.Fatal(diff)
+ }
+}
+
+const maintainersSample = `
+List of maintainers and how to submit kernel changes
+====================================================
+
+Please try to follow the guidelines below. This will make things
+easier on the maintainers. Not all of these guidelines matter for every
+trivial patch so apply some common sense.
+
+Tips for patch submitters
+-------------------------
+
+1. Always *test* your changes, however small, on at least 4 or
+ 5 people, preferably many more.
+
+< ... >
+
+8. Happy hacking.
+
+Descriptions of section entries and preferred order
+---------------------------------------------------
+
+ M: *Mail* patches to: FullName <address@domain>
+ R: Designated *Reviewer*: FullName <address@domain>
+ These reviewers should be CCed on patches.
+< ... >
+ K: *Content regex* (perl extended) pattern match in a patch or file.
+ For instance:
+ K: of_get_profile
+ matches patches or files that contain "of_get_profile"
+ K: \b(printk|pr_(info|err))\b
+ matches patches or files that contain one or more of the words
+ printk, pr_info or pr_err
+ One regex pattern per line. Multiple K: lines acceptable.
+
+Maintainers List
+----------------
+
+.. note:: When reading this list, please look for the most precise areas
+ first. When adding to this list, please keep the entries in
+ alphabetical order.
+
+3C59X NETWORK DRIVER
+M: Name1 Surname <email1@kernel.org>
+L: netdev@vger.kernel.org
+S: Odd Fixes
+F: Documentation/networking/device_drivers/ethernet/3com/vortex.rst
+F: drivers/net/ethernet/3com/3c59x.c
+
+ABI/API
+L: linux-api@vger.kernel.org
+F: include/linux/syscalls.h
+F: kernel/sys_ni.c
+X: include/uapi/
+X: arch/*/include/uapi/
+
+AD1889 ALSA SOUND DRIVER
+L: linux-parisc@vger.kernel.org
+S: Maintained
+W: https://parisc.wiki.kernel.org/index.php/AD1889
+F: sound/pci/ad1889.*
+
+PVRUSB2 VIDEO4LINUX DRIVER
+M: Name2 <email2@kernel.org>
+L: pvrusb2@isely.net (subscribers-only)
+L: linux-media@vger.kernel.org
+S: Maintained
+W: http://www.isely.net/pvrusb2/
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/driver-api/media/drivers/pvrusb2*
+F: drivers/media/usb/pvrusb2/
+
+RISC-V ARCHITECTURE
+M: Name3 <email3@kernel.org>
+M: Name4 <email4@kernel.org>
+M: Name5 <email5@kernel.org>
+L: linux-riscv@lists.infradead.org
+S: Supported
+Q: https://patchwork.kernel.org/project/linux-riscv/list/
+P: Documentation/riscv/patch-acceptance.rst
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git
+F: arch/riscv/
+N: riscv
+K: riscv
+
+THE REST
+M: Name6 <email6@kernel.org>
+L: linux-kernel@vger.kernel.org
+S: Buried alive in reporters
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+F: *
+F: */
+`