2026-03-21 17:42:31 -05:00
|
|
|
package filesearch
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-07 13:26:52 -05:00
|
|
|
"log"
|
2026-03-21 17:42:31 -05:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2026-04-07 13:26:52 -05:00
|
|
|
"runtime/debug"
|
2026-03-21 17:42:31 -05:00
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var SkipDirs = map[string]bool{
|
|
|
|
|
"$recycle.bin": true,
|
|
|
|
|
"windows": true,
|
|
|
|
|
"system volume information": true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ResultFunc func(path string)
|
|
|
|
|
|
|
|
|
|
func LookupExe(exeName string, workers int, onResult ResultFunc) {
|
2026-04-20 13:46:59 -05:00
|
|
|
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
|
2026-03-21 17:42:31 -05:00
|
|
|
if workers <= 0 {
|
|
|
|
|
workers = 8
|
|
|
|
|
}
|
|
|
|
|
lowerExe := strings.ToLower(exeName)
|
|
|
|
|
roots := driveRoots()
|
|
|
|
|
|
|
|
|
|
dirs := make(chan string, 256)
|
|
|
|
|
|
|
|
|
|
var inflight sync.WaitGroup
|
|
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
for i := 0; i < workers; i++ {
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
2026-04-07 13:26:52 -05:00
|
|
|
defer func() {
|
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
|
log.Printf("[filesearch] worker panic: %v\n%s", r, debug.Stack())
|
|
|
|
|
}
|
|
|
|
|
}()
|
2026-03-21 17:42:31 -05:00
|
|
|
for dir := range dirs {
|
|
|
|
|
walkDir(dir, lowerExe, dirs, &inflight, onResult)
|
|
|
|
|
inflight.Done()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, root := range roots {
|
|
|
|
|
entries, err := os.ReadDir(root)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, e := range entries {
|
|
|
|
|
if e.IsDir() {
|
|
|
|
|
if SkipDirs[strings.ToLower(e.Name())] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
inflight.Add(1)
|
|
|
|
|
dirs <- filepath.Join(root, e.Name())
|
|
|
|
|
} else if strings.ToLower(e.Name()) == lowerExe {
|
|
|
|
|
onResult(filepath.Join(root, e.Name()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
inflight.Wait()
|
|
|
|
|
close(dirs)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func walkDir(root, lowerExe string, dirs chan<- string, inflight *sync.WaitGroup, onResult ResultFunc) {
|
|
|
|
|
entries, err := os.ReadDir(root)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for _, e := range entries {
|
|
|
|
|
full := filepath.Join(root, e.Name())
|
|
|
|
|
if e.IsDir() {
|
|
|
|
|
if SkipDirs[strings.ToLower(e.Name())] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
inflight.Add(1)
|
|
|
|
|
select {
|
|
|
|
|
case dirs <- full:
|
|
|
|
|
default:
|
|
|
|
|
inflight.Done()
|
|
|
|
|
walkDir(full, lowerExe, dirs, inflight, onResult)
|
|
|
|
|
}
|
|
|
|
|
} else if strings.ToLower(e.Name()) == lowerExe {
|
|
|
|
|
onResult(full)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func driveRoots() []string {
|
|
|
|
|
if os.PathSeparator == '\\' {
|
|
|
|
|
var roots []string
|
|
|
|
|
for c := 'A'; c <= 'Z'; c++ {
|
|
|
|
|
root := string(c) + ":\\"
|
|
|
|
|
if info, err := os.Stat(root); err == nil && info.IsDir() {
|
|
|
|
|
roots = append(roots, root)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(roots) == 0 {
|
|
|
|
|
roots = append(roots, "C:\\")
|
|
|
|
|
}
|
|
|
|
|
return roots
|
|
|
|
|
}
|
|
|
|
|
return []string{"/"}
|
|
|
|
|
}
|