diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2026-01-19 15:15:15 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2026-01-20 21:12:57 +0000 |
| commit | fb714834adfb0e1e36c4cfc7ca288391cfc18986 (patch) | |
| tree | 47728e3c13cc37a33478e30e6a0b77f413311b8d /pkg/codesearch/codesearch.go | |
| parent | 4dc35ec28780d6a78e8afcf2650d4ada4fcd245c (diff) | |
pkg/codesearch: add dir-index command
dir-index provides a list of subdirectories and files in the given
directory in the source tree.
Diffstat (limited to 'pkg/codesearch/codesearch.go')
| -rw-r--r-- | pkg/codesearch/codesearch.go | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/pkg/codesearch/codesearch.go b/pkg/codesearch/codesearch.go index c1e99a174..8e0259af7 100644 --- a/pkg/codesearch/codesearch.go +++ b/pkg/codesearch/codesearch.go @@ -5,10 +5,13 @@ package codesearch import ( "bytes" + "errors" "fmt" "os" "path/filepath" + "slices" "strings" + "syscall" "github.com/google/syzkaller/pkg/osutil" ) @@ -26,6 +29,22 @@ type Command struct { // Commands are used to run unit tests and for the syz-codesearch tool. var Commands = []Command{ + {"dir-index", 1, func(index *Index, args []string) (string, error) { + ok, subdirs, files, err := index.DirIndex(args[0]) + if err != nil || !ok { + return notFound, err + } + b := new(strings.Builder) + fmt.Fprintf(b, "directory %v subdirs:\n", args[0]) + for _, subdir := range subdirs { + fmt.Fprintf(b, " - %v\n", subdir) + } + fmt.Fprintf(b, "\ndirectory %v files:\n", args[0]) + for _, file := range files { + fmt.Fprintf(b, " - %v\n", file) + } + return b.String(), nil + }}, {"file-index", 1, func(index *Index, args []string) (string, error) { ok, entities, err := index.FileIndex(args[0]) if err != nil || !ok { @@ -59,6 +78,8 @@ var Commands = []Command{ }}, } +var SourceExtensions = map[string]bool{".c": true, ".h": true, ".S": true, ".rs": true} + const notFound = "not found\n" func NewIndex(databaseFile string, srcDirs []string) (*Index, error) { @@ -90,6 +111,32 @@ type Entity struct { Name string } +func (index *Index) DirIndex(dir string) (bool, []string, []string, error) { + if err := escaping(dir); err != nil { + return false, nil, nil, nil + } + exists := false + var subdirs, files []string + for _, root := range index.srcDirs { + exists1, subdirs1, files1, err := dirIndex(root, dir) + if err != nil { + return false, nil, nil, err + } + if exists1 { + exists = true + } + subdirs = append(subdirs, subdirs1...) + files = append(files, files1...) + } + slices.Sort(subdirs) + slices.Sort(files) + // Dedup dirs across src/build trees, + // also dedup files, but hopefully there are no duplicates. + subdirs = slices.Compact(subdirs) + files = slices.Compact(files) + return exists, subdirs, files, nil +} + func (index *Index) FileIndex(file string) (bool, []Entity, error) { var entities []Entity for _, def := range index.db.Definitions { @@ -188,3 +235,36 @@ func formatSourceFile(file string, start, end int, includeLines bool) (string, e } return b.String(), nil } + +func escaping(path string) error { + if strings.Contains(filepath.Clean(path), "..") { + return errors.New("path is outside of the source tree") + } + return nil +} + +func dirIndex(root, subdir string) (bool, []string, []string, error) { + dir := filepath.Join(root, subdir) + entries, err := os.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + err = nil + } + var errno syscall.Errno + if errors.As(err, &errno) && errno == syscall.ENOTDIR { + err = nil + } + return false, nil, nil, err + } + var subdirs, files []string + for _, entry := range entries { + if strings.HasPrefix(entry.Name(), ".") { + // These are internal things like .git, etc. + } else if entry.IsDir() { + subdirs = append(subdirs, entry.Name()) + } else if SourceExtensions[filepath.Ext(entry.Name())] { + files = append(files, entry.Name()) + } + } + return true, subdirs, files, err +} |
